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
{{ message }}
Repository navigation
opencode completion output is truncated, producing an unparseable zsh script (unmatched ") #30831
opencode completion truncates its output mid-string before the process exits. The resulting script is not valid zsh — it ends in the middle of a quoted literal, so any shell that does eval "$(opencode completion)" in its startup files (a common pattern, and what the opencode completion help text suggests) fails on every new shell with:
(eval):22: unmatched "
This appears to be the same underlying stdout-flush race as #29866 / #26114 / #26399, but the user-visible impact is more severe: it breaks interactive shell startup for anyone who sources the completion script from their rc files, not just a missing trailing newline.
// Some subprocesses don't react properly to SIGTERM and similar signals.
// Most notably, some docker-container-based MCP servers don't handle such signals unless
// run using `docker run --init`.
// Explicitly exit to avoid any hanging subprocesses.
process.exit()
}
}finally{// Some subprocesses don't react properly to SIGTERM and similar signals.// ...// Explicitly exit to avoid any hanging subprocesses.process.exit()}
The try { await cli.parse() } block runs yargs, which for the completion subcommand writes the generated shell script directly to process.stdout. Control then falls into finally, which calls process.exit() unconditionally. Node's process.exit() does not wait for writable streams to drain — when stdout is a pipe (as with eval "$(opencode completion)"), the kernel-side pipe buffer isn't fully consumed before the process is torn down, so the tail of the output is dropped. That's how the script ends mid-string with the unmatched ".
This is consistent with the other reports of this race (#29866 JSON stream, #26114/#28606--help newline, #29330export | jq, #26399debug skill), and looks like exactly what closed PR #29340 ("fix(opencode): flush stdio before cli exit") was attempting to address.
Setting process.exitCode and letting Node exit naturally would also work, but conflicts with the existing rationale for the explicit process.exit() (hanging subprocesses), so an explicit drain-then-exit is the safer change.
I'd like to work on this. The fix should drain stdout/stderr before the process.exit() in the finally block of packages/opencode/src/index.ts. I'll add a drain-then-exit pattern as suggested, with a regression test.
Activity
ben181231 commentedon Jun 5, 2026
Quick source dive — the likely culprit is in
packages/opencode/src/index.ts:opencode/packages/opencode/src/index.ts
Lines 202 to 208 in 9211ef7
The
try { await cli.parse() }block runs yargs, which for thecompletionsubcommand writes the generated shell script directly toprocess.stdout. Control then falls intofinally, which callsprocess.exit()unconditionally. Node'sprocess.exit()does not wait for writable streams to drain — when stdout is a pipe (as witheval "$(opencode completion)"), the kernel-side pipe buffer isn't fully consumed before the process is torn down, so the tail of the output is dropped. That's how the script ends mid-string with the unmatched".This is consistent with the other reports of this race (#29866 JSON stream, #26114/#28606
--helpnewline, #29330export | jq, #26399debug skill), and looks like exactly what closed PR #29340 ("fix(opencode): flush stdio before cli exit") was attempting to address.Suggested fix
Drain stdio before exiting, e.g.:
Setting
process.exitCodeand letting Node exit naturally would also work, but conflicts with the existing rationale for the explicitprocess.exit()(hanging subprocesses), so an explicit drain-then-exit is the safer change.(Posted by Claude Code on behalf of @ben181231)
kagura-chen commentedon Jun 5, 2026
I'd like to work on this. The fix should drain stdout/stderr before the
process.exit()in thefinallyblock ofpackages/opencode/src/index.ts. I'll add a drain-then-exit pattern as suggested, with a regression test.github-actions commentedon Aug 5, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.