fix(desktop): attach child stderr tail and exit context to host-service crash reports

Merged
SA
3mo ago
+85-32

Why

Sentry DESKTOP-H1 — host-service crashed (signal SIGABRT) — is undiagnosable. The capture in host-service-coordinator.ts sent only the message plus the org id, and SIGABRT is almost certainly a native-module abort, so the child can't self-report. In packaged builds the coordinator handed the rotating log fd straight to the child (stdio: ["ignore", logFd, logFd]), so it never saw a byte of the output either.

DESKTOP-H9 — 'renderer' process exited with 'launch-failed' — has the same problem: our handler logs the full details, but the Sentry event carries none of it.

What changed

host-service crash reports

  • Child stdout/stderr are piped through the coordinator and written through to the same rotating host-service.log (dev keeps its prefixed pass-through to bun dev). Logging behaviour is unchanged; the coordinator can now see the output.
  • A 16KB rolling tail is kept per child and attached to the crash capture as outputTail, alongside pid, uptimeMs (since spawn), respawnAttempts, and version. Existing exit_code / exit_signal tags are unchanged.
  • The capture waits 500ms after exit: piped stdio drains after the process is gone, so capturing synchronously would miss exactly the abort message we're after.
  • The child's own auth secret is redacted from the tail before it leaves the process. Env is never attached.
  • error listeners on the log stream and the child's stdio, so a failed write can't take down the main process.

renderer crashes

render-process-gone now captures reason and exitCode as tags. Note this is a separate event rather than an enrichment of the SDK's own: @sentry/electron captures from an app-level listener registered at Sentry.init, which fires before this webContents listener, so that event can't be tagged retroactively.

Verification

  • bunx biome check clean on the changed files; bun run typecheck shows no new errors (pre-existing failures are unbuilt @superset/chat-legacy module resolution on main).
  • bun test src/main/lib/ — 509 pass, 0 fail.
  • Ran a standalone harness reproducing the exact spawn/capture pattern against a child that writes app logs and then process.abort()s (real SIGABRT). With the original 4KB window the ~5KB Node abort backtrace evicted the assertion line itself — that's why the tail is 16KB. At 16KB the captured tail contains the app log line, the Assertion ... failed line, and the full backtrace, with the secret rendered as [redacted] and the log file byte-identical to the child's output.

Trade-off

Production child output now flows through the main process instead of directly to a file, so a wedged main process could apply backpressure to a very chatty child once the 64KB pipe fills. Dev has always run this way, and the handlers drain unconditionally.

What the next SIGABRT will carry

outputTail (last 16KB of the child's own logs and its abort backtrace, secret-redacted), uptimeMs, respawnAttempts, pid, version, plus the existing exit code/signal tags.


Summary by cubic

Improve crash diagnostics by attaching the child process output tail and exit context to host-service crash reports. Also tag renderer process exits with reason and exit code for clearer Sentry events.

  • Bug Fixes
    • Pipe child stdout/stderr through the coordinator and write through to the rotating log; dev passthrough stays.
    • Keep a 16KB rolling output tail and attach to Sentry as outputTail with pid, uptimeMs, respawnAttempts, and app version; wait 500ms post-exit to capture drained output.
    • Redact the child’s auth secret from the tail; never include env.
    • Add error handlers on log and stdio streams so failed writes don’t crash the main process.
    • Capture renderer render-process-gone via a separate @sentry/electron event and tag with reason and exitCode.

Summary by CodeRabbit

  • Bug Fixes
    • Improved host-service crash reporting with recent process output and detailed exit information.
    • Ensured service logs are captured and closed reliably, including after startup failures.
    • Added clearer reporting for renderer process terminations, including termination reasons and exit codes.