Skip to content

fix: surface silent adapter launch failures as session errors - #1235

Merged
edwin-zvs merged 1 commit into
mainfrom
issue-1208-launch-failure
Aug 7, 2026
Merged

fix: surface silent adapter launch failures as session errors#1235
edwin-zvs merged 1 commit into
mainfrom
issue-1208-launch-failure

Conversation

@edwin-zvs

Copy link
Copy Markdown
Contributor

Fixes #1208.

Problem

When a wrapper adapter's harness child exits immediately with a fatal configuration error (e.g. grok rejecting --allow "MultiEdit(...)" with unknown tool prefix: MultiEdit), the error only reached the adapter's stderr log via emit.log. The session showed awaiting input with an empty transcript — indistinguishable from a hung agent until someone grepped the daemon log.

Change

  • adapter-common: new StderrTail (bounded, last 20 lines) + spawn_stderr_tail (same as spawn_stderr_log but retains the tail), and emit_launch_failure_if_silent — after a completed turn, if the child exited non-zero and no session event was emitted since spawn, emit a SessionEvent::Error carrying the exit status and captured stderr (or a pointer to the daemon log when stderr was empty).
  • protocol: EventEmitter now counts emitted session events (events_emitted(), shared across clones) so adapters can tell a failed turn that produced output apart from one that died silently at launch. Turns that produced real output and then failed are left alone — the transcript already shows what happened.
  • Wired into the headless turn loops of claude, codex, grok, pi, hermes, muse. Only TurnOutcome::Completed turns are checked — interrupted/stopped turns are killed by us and would misreport the kill as a launch failure. Codex keeps its existing ERROR:-line detection as the primary path; this is the fallback. Hermes keeps surfacing stdout first and falls back to the stderr tail when stdout was empty.

Regression test

crates/adapter-common launch_failure_tests: spawns a real child that prints the exact failure from #1204 to stderr and exits 2, drives it through drive_turn, and asserts exactly one SessionEvent::Error containing the stderr is emitted. Negative cases: clean exit, and non-zero exit after real output (no synthetic error). Plus tail-bounding and empty-stderr message tests.

Not in scope

  • Interactive (run_pty) launch failures — the PTY path renders the child's dying words in the terminal, and the issue's repro is headless.
  • Per-adapter tool-prefix validation/feature detection (issue's suggestion 3) — worth its own issue if wanted.

When a wrapper adapter's harness child rejects a CLI flag or fatal config
and exits before emitting a single session event, the failure only reached
the daemon log via the stderr logger; the session flipped back to awaiting
input with an empty transcript and looked hung.

Add a bounded stderr tail (spawn_stderr_tail) and a shared
emit_launch_failure_if_silent helper in adapter-common that emits a
SessionEvent::Error carrying the child's exit status and captured stderr
when a completed turn exited non-zero without producing any session
events, and wire it into the claude, codex, grok, pi, hermes, and muse
headless turn loops. EventEmitter now counts emitted session events so
adapters can tell a failed turn that produced output apart from one that
died silently at launch.

Closes #1208
@edwin-zvs
edwin-zvs merged commit bdb92a1 into main Aug 7, 2026
1 check passed
@edwin-zvs
edwin-zvs deleted the issue-1208-launch-failure branch August 7, 2026 13:07
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.

Surface adapter spawn failures as session error events

1 participant