Repository navigation
Description
Description
A provider stream dropped mid-tool-call. The call never ran, but the app shows it as a failed tool
with the raw transport error, and the card survives the automatic retry.
Timeline of the turn (v2.0.12, workspace/proxied provider): assistant text streams, then a shell
call starts streaming; the provider socket closes before the arguments finish; opencode records
retry.attempt = 2 and re-runs the step; the retry issues the same command and completes with
exit 0.
The persisted tool part for the failed attempt:
{"type":"tool","name":"shell","executed":false,"state":{"status":"error","input":{},"error":{"type":"provider.transport","message":"ECONNRESET: The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()","status":200}},"time":{"created":1790068832573,"completed":1790068919983}}state.input is empty and time.ran is absent, so the tool never executed. The app renders its
tool error card (title Shell, summary ECONNRESET) with that raw Bun hint as the detail.
Expected: a call that never ran should not be presented as a failed tool (a neutral "not run"
state at most), and runtime-internal copy should not reach users. Once the retry succeeds the
turn's message-level error is not rendered either, so this card is the only remaining trace and
reads like an unresolved shell failure.
Redacted session JSONL — failed attempt + successful retry (IDs, paths, provider/model redacted)
{"time":{"created":1790068813366,"streamed":1790068919976,"completed":1790068920159},"model":{"id":"<model>","providerID":"<workspace-provider>","variant":"max"},"content":[{"type":"reasoning","text":"<reasoning omitted>","state":{"signature":""},"time":{"created":1790068818210,"completed":1790068822920}},{"type":"text","text":"编译 + 跑相关单测,看有没有测试绑定了老分派行为:"},{"type":"tool","id":"<tool-call-id>","name":"shell","executed":false,"state":{"status":"error","input":{},"error":{"type":"provider.transport","message":"ECONNRESET: The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()","status":200}},"time":{"created":1790068832573,"completed":1790068919983}}],"finish":"error","error":{"type":"provider.transport","message":"ECONNRESET: The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()`","status":200},"retry":{"attempt":2,"at":1790068922241,"error":{"type":"provider.transport","message":"ECONNRESET: The socket connection was closed unexpectedly. For more information, pass `verbose: true` in the second argument to fetch()`","status":200}}}
{"time":{"created":1790068922399,"streamed":1790068930393,"completed":1790068939758},"model":{"id":"<model>","providerID":"<workspace-provider>","variant":"max"},"content":[{"type":"reasoning","text":"<reasoning omitted>","state":{"signature":""},"time":{"created":1790068925283,"completed":1790068930067}},{"type":"tool","id":"<tool-call-id>","name":"shell","executed":false,"state":{"status":"completed","input":{"command":"cd <repo> && go build ./... 2>&1 | head -20","timeout":180000},"content":[{"type":"text","text":"(no output)"},{"type":"text","text":"Command exited with code 0."}],"metadata":{"status":"completed","truncated":false,"exit":0}},"time":{"created":1790068930072,"ran":1790068930388,"completed":1790068939662}}],"finish":"tool-calls","rawFinish":"tool_use","cost":0.08515237800000001,"tokens":{"input":406,"output":278,"reasoning":0,"cache":{"read":304128,"write":0}}}Plugins
One local dev plugin (unrelated to this code path).
OpenCode version
2.0.12
Steps to reproduce
- Prompt a tool-capable model through a provider/proxy that can drop the socket mid-stream.
- Close the connection after
tool-input-startand before the call's arguments complete. - Let opencode retry the step. The retry succeeds, but the failed attempt's tool card remains.
Screenshot and/or share link
Screenshot of the red Shell ECONNRESET row to be attached in a comment.
Operating System
macOS (Darwin 25.4.0, arm64)
Terminal
n/a — app UI
Activity
jinhuang712 commentedon Sep 22, 2026
opencodejr commentedon Oct 28, 2026
Thanks for the detailed report and the redacted JSONL, they made this easy to follow.
What you're asking for: when the provider stream drops before a tool call's arguments are complete, the call never ran, so it shouldn't show as a failed tool with the raw transport error. At most it should be a neutral "not run" state, and it shouldn't remain as an unresolved-looking
Shell ECONNRESETcard after the automatic retry succeeds.The app currently does this on purpose. When a step's provider stream fails, every tool call that hasn't run yet is marked as an error with that step's provider error, and a retry is added as a new attempt without changing the earlier one. So this is a change to how failed attempts are shown, not a bug. I'm treating it as an enhancement request; maintainers will consider it, but there's no commitment yet. A plugin can't do this today, because plugins can't change how the app renders tool cards.
Related issues, in case they're useful:
opencode run(v2) exits 1 after a transient provider error that the runner retried and recovered from #50062: same retried ECONNRESET scenario, butopencode runexits with code 1 even though the retry worked