Skip to content

provider: opencode-go gateway returns invalid-JSON upstream response, bricking sessions until rewind #60753

Closed as not planned

Description

@SyntaxErr0r42

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

  1. Start a long session with heavy shell tool use (many ssh/heredoc commands with nested double-quote escaping).
  2. 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/systemctl output).
  3. The session step fails with the logged error below; the UI shows the same api_error string.
  4. Every automatic retry and every manual resend of the same prompt fails identically.
  5. 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 Session with AI.Error.InvalidRequest: Upstream request failed: [api_error] Upstream response was not valid JSON repeats 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 arguments string 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' ... EOS followed by && curl ...) with unterminated-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

  1. opencodejr commented on Oct 31, 2026

    @opencodejr
    Contributor

    Thanks for the detailed report and the log excerpt.

    The text Upstream request failed: … Upstream response was not valid JSON comes 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:

    1. The full error as shown for the failed step, in particular the status: and body: 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.log around Failed to drain Session are fine. Please redact anything private.
    2. Roughly how large the session was when it broke: message count, context or token usage, and whether earlier turns included images or very large tool outputs.
    3. Whether a manual /compact in a stuck session gets it working again, the way rewinding does.
    4. If you can check: whether the tool output just before the failing turn contained unusual bytes, such as NUL characters or raw binary, besides the ANSI color codes.

    The "Portable shell scanner" rejecting valid bash with unterminated-quote looks 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:

  2. SyntaxErr0r42 commented on Oct 31, 2026

    @SyntaxErr0r42
    Author

    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

  3. samuel-wise commented on Nov 2, 2026

    @samuel-wise

    Adding a counter-example to the "large/sticky history" hypothesis.

    Environment: opencode 1.19.4, Linux arm64, provider opencode-go, model qwen3.8-flash. No other provider involved in the failures.

    Observed: AI_APICallError: Upstream request failed: [api_error] Upstream response was not valid JSON

    • 5 occurrences, 2026-10-24 -> 2026-11-01, only on qwen3.8-flash; deepseek-v4.1-flash was used alongside it during the same period without this error.
    • Happened in both primary (build) and subagent (general) turns.
    • Not sticky: re-sending succeeded; no rewind, fork, or /compact required.
    • Not size- or history-correlated: one failure was on the very first request of a session whose entire history was a single 3 KB user message. The largest failing session was ~1 MB of parts. Failures occurred at steps 0, 2, 8, 13 and 22.
    • The client-side stack shows the request taking the Anthropic Messages path (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, on deepseek-v4.1-flash), and Upstream request failed: Insufficient account funds on glm-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.

  4. opencodejr commented on Nov 3, 2026

    @opencodejr
    Contributor

    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: and body: lines from the error (or from ~/.local/share/opencode/log/opencode.log), roughly how big the session was, and whether /compact gets it working again. We'll reopen and look into it. Until then, rewinding to before the failing turn or running /compact is the way to get a stuck session working again. Thanks for the detailed report.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions