Closed general

Debug-profile hydrogen.exe stack-overflows (0xC00000FD) ~3 s after window creation on Colby-ROG2; release profile runs

Colby Knox · 27d ago ·closed by John Lauer

Seen while setting up Colby-ROG2 as a build host (2026-09-25), on branch fix/89-wait-codeserver-signature (main at cff9bce3 plus changes that do not run at startup).

Symptom. The debug-profile exe (cargo build, the hd_build_rust flavor) dies about three seconds after launch, every time, with exception code 0xC00000FD (stack overflow) in hydrogen.exe itself, fault offset 0x2cdfc77. Windows Error Reporting records it and writes a dump under %LOCALAPPDATA%\CrashDumps. The log ends right after the window is created:

[on-navigation] ALL: http://localhost:1420/
[focus] hd Focused(true)

No [exit] line, no panic on stderr. The release-profile build of the same tree (cargo build --release --features custom-protocol) runs fine on the same box, and the installed 1.0.489 runs fine.

Box. Windows 11 25H2 build 26200, WSL 2.7.3, Rust 1.95 msvc, VS 2022 Build Tools, display scale that restores a 2432x1452 window. Fresh clone, fresh target/.

Open. Not bisected yet: whether main's debug build does the same on this box (John's laptop runs debug builds daily, so it is either box-specific or newer than the last debug run there), and which frame overflows (the dumps are there; the PDB is in target/debug). If it is a large stack frame in the window or layout-restore path in debug, a /STACK link arg for the dev profile, or boxing the offending state, is the likely fix.

5 Replies

John Lauer · 21d ago

Likely cause and a fix on main (bbb77117): the main thread's stack. Windows gives it 1 MB, and in a debug build the async state machines and Tauri's setup closures are many times larger than in release, which fits a debug exe dying with 0xC00000FD shortly after window creation while the release build of the same tree runs.

hydrogen-app/build.rs now links debug builds on MSVC with /STACK:8388608 (8 MB reserved; pages are committed only as used). The release profile is untouched, so shipped installers are byte-for-byte the same as before this change.

I could not reproduce it here (AdomLapper runs debug builds without the crash), so could you rebuild the debug exe on Colby-ROG2 from main and say whether it stays up? If it still dies, the frame from the dump (the PDB is in target/debug) tells us which state to box instead.

Colby Knox · 21d ago

Tested on Colby-ROG2 from main bbb77117. The /STACK:8388608 is in the exe (PE SizeOfStackReserve 8388608), and the debug build still dies the same way: 0xC00000FD about half a second after window creation.

The dump says why: the overflow is not on the main thread. cdb (WinDbg 1.2606, symbols from target\debug) shows thread 42, "tokio-rt-worker", faulting in __chkstk on entry to hydrogen_control::handle_control_inner::async_fn$0::async_block$1, under handle_control -> hyper http1 dispatch -> hydrogen_control_ai_threads::ai_tabs::start_control_server. The stack is only about 60 frames deep, so this is not recursion: it is the router's single poll frame, which in a debug build (no stack-slot reuse across the match arms) is bigger than a tokio worker's 2 MB stack. It fires on the first control-API request after the window opens.

Confirming experiment: the same debug exe launched with RUST_MIN_STACK=16777216 in its environment stays up (113 s and answering /buildinfo, dev=true). So tokio's workers take std's default stack, which that variable controls.

Two ways to fix it, your call:

  1. Debug only, smallest: in main(), before the Tauri builder starts any thread, #[cfg(debug_assertions)] set RUST_MIN_STACK to 16 MB when it is unset (std reads it once, at the first spawn). Release is untouched, and the /STACK change can come back out since the main thread was never the one overflowing.
  2. Root cause: keep the router's per-arm work out of handle_control_inner's own frame (move the heavy arms into separate non-inlined async fns). That shrinks the frame in release too, where it is smaller but still large.

Why AdomLapper does not crash is worth one check: whether RUST_MIN_STACK is set in your environment there, or your toolchain differs (this box is rustc 1.95.0).

John Lauer · 21d ago

Great dump work, thank you: my main-thread guess was wrong, and option 1 is on main as 91d08849.

  • main() now sets RUST_MIN_STACK=16777216 under #[cfg(debug_assertions)], as its first statement and only when the variable is unset, so it precedes every thread spawn and an explicit value in the environment still wins. Release builds never compile it.
  • The /STACK link arg from bbb77117 is gone from build.rs, since the main thread was never the one overflowing.

Why AdomLapper does not crash: RUST_MIN_STACK is unset there (user and machine), and its toolchain is rustc 1.93.1 against your 1.95.0, so the likeliest difference is codegen: 1.95 lays out that debug poll frame larger. I am leaving option 2 (moving the router's heavy arms into their own non-inlined async fns) for a separate change, because it touches every control route and release is not overflowing today.

Could you rebuild the debug exe from main on Colby-ROG2 and confirm it stays up with no RUST_MIN_STACK in the environment? I will close this on your confirmation.

Colby Knox · 21d ago

Confirmed on Colby-ROG2. Debug build of main 91d08849 (rebuilt, not a relink), RUST_MIN_STACK unset in the user and machine environment: hd_launch reported stillRunning after its 30 s settle, and at 60 s the process was up and /buildinfo answered 91d08849 dev=true. Before the fix the same box died with 0xC00000FD half a second after the window opened. Good to close.

John Lauer · 21d ago

Thanks Colby. Confirmed fixed by 91d08849 (debug builds set RUST_MIN_STACK to 16 MB before any thread starts; release unchanged). Closing.

Log in to reply.