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
Note: This issue was prepared with AI assistance (via prompts) using opencode and the model xai/grok-build-0.1.
Summary
opencode export <id> | jq (and similar commands) produces truncated or invalid JSON when the output is piped and the data volume is large. Writing to a file first works correctly.
This is a recurring problem. Previous reports include:
A previous attempt to address the export case (#3049) changed the implementation from console.log to process.stdout.write, but the issue persists.
Reproduction
# Fails with parse error
opencode export<large-session-id>| jq .# Works
opencode export<large-session-id>> /tmp/out.json && cat /tmp/out.json | jq .
The problem is data-size and backpressure dependent.
Workaround
Always redirect large machine-readable output to a file instead of piping directly.
Additional Context
The truncation occurs because large stdout writes are not fully flushed before the process exits. The same pattern exists across multiple CLI commands that produce significant output.
Reacted by Daniel Golding, Barbancho and Javan Makhmali
As a workaround while this is fixed: I authored Clean My Agent, which reads the session database directly rather than going through the CLI export — not affected by the pipe truncation issue for large sessions.
The process.exit() explanation in this thread doesn't hold up. Removing the process.exit() from src/index.ts entirely and waiting 2s still truncated at exactly 65536.
What's actually happening is backpressure. process.stdout.write() returns false once the pipe buffer is full, and the queued remainder is dropped when the process exits. console.log does the same and gives you nothing to await.
That explains why #3049 didn't fix it: it moved export to process.stdout.write but never waited for the write to be accepted, so the second half of the problem was still there.
It also affects opencode db and opencode session list --format json, not just export. Repro that needs no existing session:
Q="with recursive t(n) as (select 1 union all select n+1 from t where n < 5000) select n as n, '0123456789012345678901234567890123456789' as pad from t"
opencode db --format json "$Q"| wc -c # 65536
opencode db --format json "$Q"> f; wc -c f # 388896
Exit code is 0 both times. Worth flagging for TSV specifically: truncated output still parses, so a consumer just sees a smaller table and no error.
Opened #39577 with a fix (await drain, EPIPE resolves so | head still works).
One separate thing I hit while narrowing this down, which may be a Bun bug rather than ours: console.log drops everything past 64 KiB once @babel/core is in the module graph, even on a natural exit with no process.exit() involved. packages/opencode/bunfig.toml preloads @opentui/solid/preload, which pulls in Babel.
bun add @babel/core
echo'console.log("x".repeat(200000))'> main.ts
echo'import "@babel/core"'> pre.ts
bun main.ts | wc -c # 200001
bun --preload ./pre.ts main.ts | wc -c # 65536
Tested on opencode 1.18.7 and dev, bun 1.3.11, macOS arm64.
Opened #46912: it waits for the process.stdout.write callback (the way generate.ts already does) in export, session list --format json and db --format json, and treats EPIPE as done. Repro numbers are in the PR.
Another instance of this systemic issue, now with a deterministic minimal reproduction and a runtime-level root cause. Affected command: opencode api.
opencode version: 2.0.22
OS: Linux x64
Shell: bash
Reproduction (opencode api)
# A consumer that does not drain immediately -> truncated to exactly the pipe capacity
opencode api get /openapi.json | (sleep 1; wc -c)
# actual: 65536# expected: 256617# Redirecting always works
opencode api get /openapi.json > /tmp/x.json; wc -c /tmp/x.json
# => 256617# Under concurrent load, with a fast consumerforiin 1 2 3 4 5 6;do (opencode api get /openapi.json | wc -c) &done;wait# => 256617 256617 256617 131072 256617 256617
The truncation point is always exactly the pipe capacity: 64 KiB (single, slow consumer) or 128 KiB (concurrent). Redirect-to-file never truncates because regular-file writes are never short.
Root cause
Two parts combine:
packages/cli/src/commands/handlers/api.ts writes without awaiting the flush:
This is Bun-specific. In Bun, process.exit() discards stdout bytes that are still buffered for a pipe. Node drains stdout before exiting, so the same code is fine under Node.
Minimal Bun repro (runtime level)
// truncates to pipe capacityprocess.stdout.write("A".repeat(300000)+"\n");process.exit(0)// => 65536// works (natural exit flushes)process.stdout.write("A".repeat(300000)+"\n")// => 300001// works (await the actual data write, then exit)awaitBun.write(Bun.stdout,"A".repeat(300000)+"\n");process.exit(0)// => 300001
Things that do not help under Bun (all still truncate to 65536):
// awaiting a follow-up empty writeprocess.stdout.write(BIG)awaitnewPromise((r)=>process.stdout.write("",()=>r()))process.exit(0)// flushing with an empty Bun.writeprocess.stdout.write(BIG);awaitBun.write(Bun.stdout,"");process.exit(0)// even a delay before exitingprocess.stdout.write(BIG);awaitnewPromise((r)=>setTimeout(r,50));process.exit(0)
Only a natural exit, or writing the data itself through an awaited Bun.write(Bun.stdout, ...), flushes it.
Suggested fix
Either:
stop forcing process.exit() in packages/cli/src/index.ts (assign process.exitCode and let the runtime exit, which flushes), or
write large output with an awaited Bun.write(Bun.stdout, ...) in api.ts and the other large-output handlers.
Both were validated against the Bun repro above. This is the same class of failure as the export truncation reported here, so a shared stdout-write helper that awaits completion would fix all of these at once.
Activity
github-actions commentedon May 26, 2026
This issue might be a duplicate of an existing issue. Please check:
opencode debug skillreturns before output is complete #26399: Same root cause and symptom — stdout output truncated/invalid JSON when piping opencode commands tojqdue toprocess.exit()before stdout drains.If this is indeed the same underlying bug, consider adding details or a +1 to #26399 instead.
blain3white commentedon Jun 9, 2026
As a workaround while this is fixed: I authored Clean My Agent, which reads the session database directly rather than going through the CLI export — not affected by the pipe truncation issue for large sessions.
jornado commentedon Jul 29, 2026
The
process.exit()explanation in this thread doesn't hold up. Removing theprocess.exit()fromsrc/index.tsentirely and waiting 2s still truncated at exactly 65536.What's actually happening is backpressure.
process.stdout.write()returnsfalseonce the pipe buffer is full, and the queued remainder is dropped when the process exits.console.logdoes the same and gives you nothing to await.That explains why #3049 didn't fix it: it moved
exporttoprocess.stdout.writebut never waited for the write to be accepted, so the second half of the problem was still there.It also affects
opencode dbandopencode session list --format json, not justexport. Repro that needs no existing session:Exit code is 0 both times. Worth flagging for TSV specifically: truncated output still parses, so a consumer just sees a smaller table and no error.
Opened #39577 with a fix (await drain, EPIPE resolves so
| headstill works).One separate thing I hit while narrowing this down, which may be a Bun bug rather than ours:
console.logdrops everything past 64 KiB once@babel/coreis in the module graph, even on a natural exit with noprocess.exit()involved.packages/opencode/bunfig.tomlpreloads@opentui/solid/preload, which pulls in Babel.Tested on opencode 1.18.7 and dev, bun 1.3.11, macOS arm64.
andrescera commentedon Sep 2, 2026
Opened #46912: it waits for the process.stdout.write callback (the way generate.ts already does) in export, session list --format json and db --format json, and treats EPIPE as done. Repro numbers are in the PR.
fix(opencode): wait for stdout writes before exit so piped JSON is no…
fix(opencode): wait for stdout writes before exit so piped JSON is no…
fix(opencode): wait for stdout writes before exit so piped JSON is no…
chenweidu666 commentedon Oct 3, 2026
Another instance of this systemic issue, now with a deterministic minimal reproduction and a runtime-level root cause. Affected command:
opencode api.Reproduction (
opencode api)The truncation point is always exactly the pipe capacity: 64 KiB (single, slow consumer) or 128 KiB (concurrent). Redirect-to-file never truncates because regular-file writes are never short.
Root cause
Two parts combine:
packages/cli/src/commands/handlers/api.tswrites without awaiting the flush:packages/cli/src/index.ts:136forces exit as soon as the handler returns:This is Bun-specific. In Bun,
process.exit()discards stdout bytes that are still buffered for a pipe. Node drains stdout before exiting, so the same code is fine under Node.Minimal Bun repro (runtime level)
Things that do not help under Bun (all still truncate to 65536):
Only a natural exit, or writing the data itself through an awaited
Bun.write(Bun.stdout, ...), flushes it.Suggested fix
Either:
process.exit()inpackages/cli/src/index.ts(assignprocess.exitCodeand let the runtime exit, which flushes), orBun.write(Bun.stdout, ...)inapi.tsand the other large-output handlers.Both were validated against the Bun repro above. This is the same class of failure as the
exporttruncation reported here, so a shared stdout-write helper that awaits completion would fix all of these at once.thdxr commentedon Oct 11, 2026
Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (
opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.— from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊