You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
opencode run -m <invalid-model> "<prompt>" printed the error but exited 0,
breaking shell scripts that key off $?. Without a prompt the same command
exits 1, so the bug only hits the prompt path.
In run.ts, the non-interactive path runs loop() as fire-and-forget. When a session.error event arrives, the handler stores the message locally and prints
it — but never propagates it to the process exit code. After the session goes
idle the loop breaks, the event subscription closes, the event loop drains, and
Node exits 0.
Fix: set process.exitCode = 1 in the session.error branch. Same pattern used
elsewhere in the CLI for soft-failure exits (thread.ts:126,222, attach.ts:54,81). Using exitCode rather than process.exit(1) lets the loop
finish, the subscription tear down, and stdout flush before Node exits — relevant
for --format json consumers.
Refs older duplicates that were never actually closed by a fix: #15558, #17854, #2489. PR #15787 took a different approach (await loopDone then process.exit(1))
but was auto-closed by the template-compliance bot before any human review.
How did you verify your code works?
Manual repro on dev tree (also reproduces on installed 1.14.41):
```
$ bun --conditions=browser packages/opencode/src/index.ts run -m invalid-model "test"
Error: Model not found: invalid-model/
$ echo $?
1 # was 0 before the fix
```
No-prompt path still exits `1` (no regression):
```
$ bun --conditions=browser packages/opencode/src/index.ts run -m invalid-model </dev/null
Error: You must provide a message or a command
$ echo $?
1
```
`bun run typecheck` passes; `bunx oxlint` reports no new warnings.
This PR also addresses setting non-zero exit codes for the run command, specifically when errors are encountered. It may be handling a similar or related issue to the current PR fix(run): set non-zero exit code on session error #26588.
The current PR (26588) appears to be addressing a specific edge case where session.error events weren't propagating to the process exit code in the non-interactive/prompt path, whereas PR #14625 may have handled a broader set of error scenarios. These could be complementary fixes or addressing different aspects of the same underlying issue.
For visibility: #14625 (open since 2026-02-22) takes a different approach to the same bug — it converts loop() into an awaitable, awaits it after the prompt is submitted, and then sets process.exitCode based on the outer error variable. Both fixes produce the same end behavior; this one is smaller (+1 line) and doesn't change the control flow. Happy to defer if maintainers prefer the other approach.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue for this PR
Closes #26509
Type of change
What does this PR do?
opencode run -m <invalid-model> "<prompt>"printed the error but exited0,breaking shell scripts that key off
$?. Without a prompt the same commandexits
1, so the bug only hits the prompt path.In
run.ts, the non-interactive path runsloop()as fire-and-forget. When asession.errorevent arrives, the handler stores the message locally and printsit — but never propagates it to the process exit code. After the session goes
idle the loop breaks, the event subscription closes, the event loop drains, and
Node exits
0.Fix: set
process.exitCode = 1in thesession.errorbranch. Same pattern usedelsewhere in the CLI for soft-failure exits (
thread.ts:126,222,attach.ts:54,81). UsingexitCoderather thanprocess.exit(1)lets the loopfinish, the subscription tear down, and stdout flush before Node exits — relevant
for
--format jsonconsumers.Refs older duplicates that were never actually closed by a fix: #15558, #17854,
#2489. PR #15787 took a different approach (await
loopDonethenprocess.exit(1))but was auto-closed by the template-compliance bot before any human review.
How did you verify your code works?
Manual repro on dev tree (also reproduces on installed 1.14.41):
```
$ bun --conditions=browser packages/opencode/src/index.ts run -m invalid-model "test"
Error: Model not found: invalid-model/
$ echo $?
1 # was 0 before the fix
```
No-prompt path still exits `1` (no regression):
```
$ bun --conditions=browser packages/opencode/src/index.ts run -m invalid-model </dev/null
Error: You must provide a message or a command
$ echo $?
1
```
`bun run typecheck` passes; `bunx oxlint` reports no new warnings.
Screenshots / recordings
N/A — CLI exit code change.
Checklist