Repository navigation
Description
Summary
Sessions using the opencode-go (Zen) provider intermittently die with Upstream request failed: [api_error] Upstream response was not valid JSON during session drain. The failure is sticky: every retry resends the same history and fails identically, so the conversation is bricked until the user manually rewinds/edits earlier messages. Failures cluster around assistant turns whose tool calls contain dense escape sequences (heredocs, nested \" quoting, ANSI control codes in tool results).
Environment
- opencode version: 2.2.3 (latest)
- OS: Linux 5.14.0 x86_64 (Rocky EL9)
- Terminal: KDE Konsole, TERM=xterm-256color, COLORTERM=truecolor
- Shell: /bin/bash
- Install/channel: latest (bundled binary in ~/.opencode/bin)
- Active plugins: none configured
- Provider/model: opencode-go (Zen gateway), qwen3.8-flash
Reproduction
- Start a long session with heavy
shelltool use (many ssh/heredoc commands with nested double-quote escaping). - Continue until the assistant produces a turn with tool-call arguments containing dense escapes, or the preceding tool results contain ANSI control characters (e.g. colored
puppet/systemctloutput). - The session step fails with the logged error below; the UI shows the same
api_errorstring. - Every automatic retry and every manual resend of the same prompt fails identically.
- Only rewinding the conversation (fork/edit) to before the offending turn makes requests succeed again.
Not 100% deterministic, but ~17 occurrences in a single evening of one session; the stuck-loop behaviour was deterministic once broken.
Expected Behavior
- Provider stream errors should be retryable without permanently bricking the session, and/or the client should offer automatic repair of the offending history entry.
Actual Behavior
Failed to drain SessionwithAI.Error.InvalidRequest: Upstream request failed: [api_error] Upstream response was not valid JSONrepeats on every attempt against the same history.
Additional Context
Excerpt from ~/.local/share/opencode/log/opencode.log (stack identical across occurrences):
level=ERROR message="Failed to drain Session" cause="AI.Error: Upstream request failed:
[api_error] Upstream response was not valid JSON
at ... [cause]: AI.Error.InvalidRequest: Upstream request failed:
[api_error] Upstream response was not valid JSON" sessionID=ses_***
Observations that may help isolate the cause:
- The gateway reports the upstream model response was not valid JSON — consistent with the model emitting a tool-call
argumentsstring containing an invalid JSON escape (patterns in these turns include\\\",$'...', raw ANSI CSI sequences copied from prior tool output, and single tool-call arguments several KB long). - A related, separate symptom in the same session: the local "Portable shell scanner" rejected valid bash (
ssh host 'bash -s' <<'EOS' ... EOSfollowed by&& curl ...) withunterminated-quote, so some turns were malformed at the harness level before even reaching the provider. - Workaround that helped: keeping tool-call arguments quote-light (stdin scripts, one simple command per line, stripping ANSI from output) drastically reduced the frequency.
- Happy to provide the full session storage/logs privately for reproduction.
Activity
opencodejr commentedon Oct 31, 2026
Thanks for the detailed report and the log excerpt.
The text
Upstream request failed: … Upstream response was not valid JSONcomes from the OpenCode Go gateway, not from the OpenCode client. The gateway has used that same message for quite different failures before, such as a request that was too large (HTTP 413) or a used-up usage limit. A failure that comes back every time on the same history also suggests the problem is in the request being sent, not in the model's output. To narrow it down, could you share:status:andbody:lines. Since v2.0.20 OpenCode stores the HTTP status and the provider's raw response body with the error. If the UI doesn't show them, the matching lines in~/.local/share/opencode/log/opencode.logaroundFailed to drain Sessionare fine. Please redact anything private./compactin a stuck session gets it working again, the way rewinding does.The "Portable shell scanner" rejecting valid bash with
unterminated-quotelooks like a separate problem. A separate issue with the exact command would help.You can reply here with the details, or edit the issue. If you edit it, please also leave a comment saying you've updated it so we know you're done.
Related issues, ordered from most to least likely to be the same problem:
opencode-goroute-dependent limits causing sticky failures in long sessionsSyntaxErr0r42 commentedon Oct 31, 2026
After spending some time on this, I've concluded that this issue is a result of the model creating nested heredocs and other complexly structured quoted commands, usually around remote execution of commands via ssh as part of the task given in a prompt but lacking specific guard rails. I've since created a skill which directs the model (Qwen3.8 Flash) to adhere to specific instructions on how to perform remote command executions in a manner that will hopefully lessen these issues. I am testing that skill right now and can share its efficacy if requested.
TL;DR this is a skill issue, or rather a lack of guardrails and just letting the model figure things out on its own. Below is a list of the specific logs/requestIDs of the affected calls:
01a19336-1181-7cb9-aaec-8a89197e3bac
01a19333-3ab5-7d83-b004-62db54fcd239
01a19000-58c1-7a8b-9a8b-9d05d2522273
01a19000-0f9b-726f-ab0b-e035c329d401
01a18fff-b6f8-7ee8-a29c-54de0c36eb1c
01a18ff8-1194-755c-bc36-ad324c227914
01a18ff4-4e1d-713d-87f5-0b896a181558
01a18f67-ec60-731e-aa2d-43ecbe6470fe
01a18f64-aded-7a52-92fa-a9424ea70692
01a18f61-4b2c-7199-82e1-abc94281d6ac
01a18f52-947a-773e-94bb-8ab4430efdbc
01a18f51-bf6a-7efb-8abc-93fec795a0f2
01a18f51-5299-7f7b-bf24-ce2dbc816150
01a18e83-8f43-77b1-97bf-82403a5491e7
01a18e49-b3f1-7fcb-8ae8-215159f4df82
01a18e3d-cec0-7463-aeb1-7ea1c3d0ba19
01a18e37-6dd1-7d31-914f-31d88f5f59ce
01a18e2d-5c3f-7771-9ac4-84fefa7a6cfd
01a18e2b-a3d8-72b5-8ffa-30e20e4d8a94
01a18e2b-073b-79e3-bf94-cf3f8b6adb03
01a18e1b-1bbf-7fbf-8c9e-191203c12c70
01a18dca-f424-781a-917c-356b65eea622
01a18dc6-7a3b-7267-9d2d-21a7a38c40dd
01a18dae-d2fe-7b7e-a19c-315875392829
01a18dac-a8c4-790b-8af6-2e3019230836
01a18daa-83ca-7d87-8e0b-ae9309a4f723
01a18da5-bbec-753e-8529-09b1610b18a6
01a18da4-9572-7689-8045-6d5b91462dfc
01a18da3-f04e-7bf2-b3d0-af64a0213708
01a18d94-fae9-75f8-864b-f519f9236571
01a18d94-10a0-7545-8fb5-f09fc1a25b71
01a18d93-aed2-77dc-a3c2-d8ee03b2afe9
samuel-wise commentedon Nov 2, 2026
Adding a counter-example to the "large/sticky history" hypothesis.
Environment: opencode 1.19.4, Linux arm64, provider
opencode-go, modelqwen3.8-flash. No other provider involved in the failures.Observed:
AI_APICallError: Upstream request failed: [api_error] Upstream response was not valid JSONqwen3.8-flash;deepseek-v4.1-flashwas used alongside it during the same period without this error.build) and subagent (general) turns./compactrequired.AnthropicMessagesLanguageModel2.doStream), i.e. the gateway's upstream returned a body that was not valid JSON.Other gateway messages on the same key during this period (possibly related):
[ttft_admission_refused] the cell is at capacity; retry shortly (after N s)(22x, ondeepseek-v4.1-flash), andUpstream request failed: Insufficient account fundsonglm-5.3-flash/gpt-5.4-nano. The capacity refusals suggest gateway/upstream flakiness.The first-request / tiny-history failure suggests this can occur without any prior tool output, so it may not be limited to dense-escape tool history. Happy to provide redacted details (status/body, sizes) if useful.
opencodejr commentedon Nov 3, 2026
We haven't heard back, so I'm closing this for now. Without the HTTP status and raw response body of the failed step, we can't tell what the OpenCode Go gateway is rejecting. That message has hidden other problems before: a request that was too large (#53107) and a used-up usage limit (#50352). Stray bytes in tool output stored in the history can also cause it (#58354).
If it happens again, please reply here with the
status:andbody:lines from the error (or from~/.local/share/opencode/log/opencode.log), roughly how big the session was, and whether/compactgets it working again. We'll reopen and look into it. Until then, rewinding to before the failing turn or running/compactis the way to get a stuck session working again. Thanks for the detailed report.