Skip to content

fix: drain pending events before breaking on session idle in JSON format mode#31446

Open
jangel97 wants to merge 1 commit into
anomalyco:devfrom
jangel97:dev
Open

fix: drain pending events before breaking on session idle in JSON format mode#31446
jangel97 wants to merge 1 commit into
anomalyco:devfrom
jangel97:dev

Conversation

@jangel97

@jangel97 jangel97 commented Jun 9, 2026

Copy link
Copy Markdown

Issue for this PR

Closes #31435

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

opencode run --format json exits immediately when session.status = idle fires, but in containerized/CI environments the idle event races ahead of text and step-finish part events in the SSE pipeline. The result is truncated JSONL output — only step_start is emitted while text and step_finish are lost.

The fix tracks active steps with a counter (activeSteps). When idle arrives while steps are still open, it defers the break (pendingIdle = true) and continues draining events until all step-finish events arrive. A 3-second safety timeout ensures the loop doesn't hang if a step-finish is never delivered.

I understand why this works: the idle event is published by the Runner's onIdle callback after the processor finishes, but it travels through a different path (session status → PubSub) than part events (LLM stream → processor → PubSub). In containerized environments with higher event delivery latency, idle consistently wins the race. By deferring the break and waiting for the step lifecycle to complete, we guarantee all parts are consumed before exiting.

How did you verify your code works?

Tested in three configurations:

  1. From source (macOS): bun run --conditions=node src/index.ts run --format json -m google-vertex-anthropic/claude-sonnet-4@20250514 "Say hello" — all 3 events emitted (step_start, text, step_finish)
  2. Compiled binary in container (UBI10): patched binary with OPENCODE_EXPERIMENTAL=true — all 3 events emitted
  3. Unpatched binary in container (control): only step_start emitted, confirming the race condition

Also verified:

  • Non-JSON mode (opencode run "prompt") works unchanged
  • The 3s timeout fires correctly when simulated (step-finish withheld)

Screenshots / recordings

Before fix (container): only step_start emitted

{"type":"step_start","timestamp":...,"part":{"type":"step-start",...}}

After fix (container): all events emitted

{"type":"step_start","timestamp":...,"part":{"type":"step-start",...}}
{"type":"text","timestamp":...,"part":{"type":"text","text":"Hello! I'm OpenCode...",...}}
{"type":"step_finish","timestamp":...,"part":{"type":"step-finish","tokens":{"total":12997,...},...}}

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

…mat mode

The loop() function in opencode run --format json breaks immediately
when it receives a session.status = idle event. In containerized
environments, this event races ahead of text and step-finish part
events in the SSE pipeline, causing incomplete JSONL output (only
step_start emitted).

Track active step lifecycle with a counter. When idle arrives while steps
are still open, defer the break and drain remaining events with a 3s
safety timeout.
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.

opencode run --format json drops text and step-finish events in containerized environments

1 participant