Skip to content

fix(cli): surface fatal [STARTUP] boot diagnostics without --log (#13314) - #13779

Merged
diegosouzapw merged 3 commits into
release/v3.8.51from
fix/13314-windows-500-every-route-better
Sep 16, 2026
Merged

diegosouzapw merged 3 commits into
release/v3.8.51from
fix/13314-windows-500-every-route-better

Conversation

@diegosouzapw

Copy link
Copy Markdown
Owner

Refs #13314

This PR fixes the confirmed SIDE defect behind the "zero output anywhere" part of #13314:
ServerSupervisor's default (non --log) mode swallows a fatal [STARTUP] Fatal: ... boot
diagnostic until the child process exits — which never happens if the HTTP listener still comes
up after the fatal failure, so every route 500s with no visible diagnostic. It does not
resolve the reporter's underlying Windows-specific "why did the DB driver cascade fail" question,
which remains needs-info pending a --log re-run from the reporter, and does not touch the
separate EADDRINUSE supervisor/server race already covered by open PR #12485.

Not covered here

Root cause

bin/cli/runtime/processSupervisor.mjs::ServerSupervisor.start() spawns the server child with
piped (non-inherited) stdio whenever --log/OMNIROUTE_SHOW_LOG is off (the default).
bufferOutput() only pushes every line into an in-memory 50-line ring buffer; that buffer is
flushed to the real console via dumpCrashLog() only on process exit or a readiness-timeout.
src/instrumentation-node.ts/src/instrumentation.ts already print an unconditional
console.error("[STARTUP] Fatal: ...") for boot failures (#7773/#10171), but if the HTTP listener
still comes up afterward (satisfying the readiness probe), the supervisor never sees an exit and
that fatal line is captured into the buffer but never shown. There was exactly one existing
carve-out from this rule (isFatalInstrumentationHookFailure, an Android/Termux-specific string
match added for #10028) — not generalized to the [STARTUP] Fatal: prefix used by every other
fatal boot guard.

Fix

  • bin/cli/utils/ensureAndroidCacheDir.mjs: added isFatalStartupDiagnostic(text), matching the
    /^\[STARTUP\] Fatal:/m prefix already used consistently by every fatal boot guard.
  • bin/cli/runtime/processSupervisor.mjs::bufferOutput(): alongside the existing
    isFatalInstrumentationHookFailure check, force-print the line to process.stderr immediately
    (once per process run) when isFatalStartupDiagnostic(text) matches — mirroring the existing
    Android carve-out pattern. The ring-buffer/dumpCrashLog() behavior for everything else is
    unchanged; this is a narrow, additive carve-out.

Regression test

tests/unit/cli-supervisor-surfaces-fatal-startup-diagnostic-13314.test.ts

RED (unfixed code):

✖ ServerSupervisor surfaces a fatal [STARTUP] Fatal: boot diagnostic to the real console even when the child never exits (default, non --log mode)
  AssertionError [ERR_ASSERTION]: expected the fatal boot diagnostic to reach the real console even though the child process never exits
    expected: /\[STARTUP\] Fatal: Database driver initialization failed/

GREEN (fixed code):

[STARTUP] Fatal: Database driver initialization failed: better-sqlite3 invalid, node:sqlite fallback also failed
✔ ServerSupervisor surfaces a fatal [STARTUP] Fatal: boot diagnostic to the real console even when the child never exits (default, non --log mode)
ℹ tests 1
ℹ pass 1
ℹ fail 0

Gates run

  • npx eslint --suppressions-location config/quality/eslint-suppressions.json <changed files> — 0 errors (the two .mjs files are eslint-ignored by config, expected; the new test file is clean)
  • node scripts/check/check-file-size.mjs — 1 pre-existing violation (open-sse/utils/stream.ts, untouched by this PR, confirmed present on origin/release/v3.8.51) — no violation on files touched here
  • node scripts/check/check-complexity.mjs — OK, 2824 violations (baseline 3218)
  • node scripts/check/check-cognitive-complexity.mjs — OK
  • node scripts/check/check-test-discovery.mjs — OK, new test discovered
  • Existing tests of the touched area (tests/unit/cli-process-supervisor.test.ts,
    tests/unit/cli-process-supervisor-spawn-error-8091.test.ts,
    tests/unit/termux-android-cache-dir.test.ts,
    tests/unit/cli-serve-readiness-timeout-6321.test.ts) — all pass, no alignment needed

Existing tests aligned

None — all pre-existing supervisor/Android-cache tests pass unmodified.

diegosouzapw and others added 3 commits September 15, 2026 15:31
)

ServerSupervisor's default (non --log) mode pipes the server child's
stdout/stderr into an in-memory ring buffer that is only flushed to the
real console when the child process exits or a readiness timeout fires.
If the HTTP listener still comes up after a fatal `[STARTUP] Fatal: ...`
boot diagnostic was already printed (e.g. the better-sqlite3/node:sqlite
driver cascade failing hard), that line is captured but never shown --
every route then 500s with zero visible diagnostic anywhere. Generalizes
the existing Android/Termux-specific force-print carve-out
(isFatalInstrumentationHookFailure, #10028) to any `[STARTUP] Fatal:`-
prefixed guard.

Regression test:
tests/unit/cli-supervisor-surfaces-fatal-startup-diagnostic-13314.test.ts
@diegosouzapw
diegosouzapw merged commit 0459c6d into release/v3.8.51 Sep 16, 2026
19 of 21 checks passed
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…gosouzapw#13314) (diegosouzapw#13779)

Merged in the 2026-09-16 sweep of the maintainer's own open PRs, at the owner's explicit instruction. No push was made to the PR branch: the merge took the head as the owning session left it (verified OPEN, non-draft and MERGEABLE against the release tip immediately before merging).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant