Repository navigation
Description
Summary
On Windows, when the varlock CLI child process dies abnormally (in our case from a Node.js libuv teardown assertion, exit code 0xC0000409 / STATUS_STACK_BUFFER_OVERRUN), varlock/auto-load calls process.exit() in the parent process with that raw abort code and prints nothing useful. The host tool that imported varlock/auto-load (for us the Alchemy CLI, but any framework/build script is affected) dies mid-command with exit code -1073740791 and zero diagnostics.
Environment
- Windows 11
- Node v24.18.0 (reproduced on v24.19.0; works on v24.20.0+ and v22.23.3)
- varlock 1.18.0
- Host: alchemy 2.0.0-beta.81 (bun 1.3.14)
Repro
In any project where varlock/auto-load is imported (our case: the IaC entry file does import "varlock/auto-load"), run the CLI child directly:
node_modules\.bin\varlock load --format json-full --compactOutput: the normal JSON is printed to stdout, then:
Assertion failed: !(handle->flags & UV_HANDLE_CLOSING), file src\win\async.c, line 94
Exit code: -1073740791 (0xC0000409).
Through auto-load, the same child failure kills the parent process. Running alchemy logs produced exactly one line of output before dying:
INFO: Importing stack module
Assertion failed: !(handle->flags & UV_HANDLE_CLOSING), file src\win\async.c, line 94
exit: -1073740791
The host never got to print any error of its own. (node_modules\.bin\varlock.exe is Bun's bin shim; it resolves node from PATH, which is why the installed Node version decides whether this fires. Pinning an older Node on PATH makes it exit 0 — which is how we currently work around it.)
Root cause on the varlock side
dist/auto-load.mjs's catch block:
} catch (err) {
if (err instanceof VarlockExecError && err.stderr) process.stderr.write(err.stderr);
else console.error(err);
const exitCode = err.exitCode ?? 1;
...
process.exit(exitCode); // <-- propagates the child's 0xC0000409 to the host
}When the child dies with a native abort code, err.stderr is empty (the libuv assertion goes to the C runtime, not through the pipe), so the host gets no error message at all — just a silent process.exit(-1073740791).
Requested fixes
- Do not propagate raw Windows abort codes (>= 0xC0000000) as the host's exit code. Map them to
1and print a clear message, e.g.varlock env resolution failed: the varlock CLI crashed (exit code 0xC0000409). Run "varlock load" manually to see the underlying error. - Include the failing child's command line and exit status in that message.
- Consider also surfacing the child's
stdouton failure — in this mode the CLI actually produced its full JSON output before aborting, which is valuable context. - Related: auto-load: DEP0190 warning on Windows from execFileSync(varlock.cmd, args, { shell: true }) #1137 covers the Windows
execFileSync(varlock.cmd, args, { shell: true })fallback in the same helper.
Note for triage: the underlying Node assertion is a Node bug (nodejs/node#64322 family) that was fixed in Node v24.20.0 — bisected: v24.18.0 and v24.19.0 crash, v24.20.0 exits 0. The bug above is the reason a single crashing child takes down every embedding tool with an opaque exit code, regardless of the cause.
Activity
pullfrog commentedon Oct 10, 2026
pullfrog commentedon Oct 10, 2026
Implemented in PR #1210: auto-load maps native CLI crash statuses to host exit code
1and reports the invoked command and original status, while keeping potentially secret-bearing stdout available to error hooks rather than printing it. All 2,438 Varlock tests passed (13 skipped), along with lint, typecheck, package build, and docs build; Windows crash statuses were simulated on Linux, not reproduced natively.Task list (4/4 completed)
gpt-6.1-sol| 𝕏theoephraim commentedon Oct 10, 2026
Thanks for this. Will get it sorted out asap.
github-actions commentedon Oct 13, 2026
✅ A fix has been merged to
mainin #1210 and will ship in the next release. GitHub auto-closed this issue on merge, but the change isn't published yet.