anomalyco / anomalyco/opencode
`opencode run --format=json` emits terminal `step_finish` but never exits
@nexxeln is already working on this.
Since Aug 25, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
Description
In non-interactive mode, opencode run --format=json can finish the primary session, emit a terminal step_finish event with reason: "stop", and then remain alive indefinitely.
The model has finished, all tool calls are complete, and no additional output or provider requests occur. The process still does not return control to its parent and must be terminated externally.
This is a general CLI lifecycle issue affecting scripts, CI jobs, subprocess callers, and other automation that expects opencode run to exit after completion.
Actual behavior
The final JSON event is a successful terminal event:
{
"type": "step_finish",
"sessionID": "ses_...",
"part": {
"type": "step-finish",
"reason": "stop"
}
}
After emitting this event:
- No further JSON events are written.
- No additional model requests are made.
- stdin is already closed.
- No permission prompt is pending.
- The opencode process remains alive.
- The external timeout command eventually terminates it and returns exit code 124.
Expected behavior
After the primary session emits step_finish with reason: "stop":
- OpenCode should complete any pending output flush.
- The event subscription and local server resources should be closed.
- opencode run should exit promptly with code 0.
A structured terminal error should similarly close resources and exit with a nonzero status.
Suspected lifecycle problem
In packages/opencode/src/cli/cmd/run.ts, the JSON event loop emits step_finish when it observes a step-finish part, but the loop only breaks after a separate session.status event with status idle.
The command also awaits client.session.prompt(...) before awaiting completion of the event consumer.
This appears to leave at least two possible hang paths:
-
A terminal step-finish event is emitted, but session.status: idle is not observed.
-
The terminal event is emitted, but the outstanding client.session.prompt(...) request does not settle.
The non-interactive command should have one coordinated terminal lifecycle:
terminal completion should settle the prompt operation, stop the event subscription, release local server/runtime handles, and return the correct exit code.
Suggested regression test
Add a process-level test that:
-
Spawns opencode run --format=json as a child process.
-
Runs a tool-using session.
-
Confirms that the primary session emits:
{"type":"step_finish","part":{"reason":"stop"}}
-
Requires the child process to exit with code 0 within a short grace period.
-
Fails if the terminal event is emitted but the child remains alive.
Testing only the internal session state would not catch leaked handles or an unresolved CLI request; the test should observe the actual child process.
Related issues
-
#17516 — opencode run hangs after completing tool calls; reported against 1.2.26 and closed as not planned.
-
#26220 — related infinite-loop symptoms after tool completion, although this report is specifically about the process remaining alive after an explicit terminal reason: "stop" event.
This distinguishes the report from permission hangs, API retry loops, or models that never finish. The critical evidence is: **OpenCode itself emitted its successful terminal event, but the CLI process did not honor it.
Plugins
No response
OpenCode version
1.18.21
Steps to reproduce
Run OpenCode non-interactively with a prompt that requires several tool calls:
timeout 600s opencode \
--model=<provider>/<model> \
run \
--format=json \
--thinking \
--dangerously-skip-permissions \
-- "Inspect this repository, implement the requested fix, and run the relevant tests." \
</dev/null >opencode.jsonl 2>opencode.stderr
echo "exit code: $?"
tail -n 5 opencode.jsonl
The issue is intermittent depending on the task, but occurred in four out of five independent tool-using runs.
### Screenshot and/or share link
_No response_
### Operating System
_No response_
### Terminal
_No response_
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.