Repository navigation
Description
Description
On the built-in opencode-go provider with deepseek-v4.1-flash, requests frequently sit with a spinner and no bytes arriving, sometimes for 2-3 minutes, then either answer late or get interrupted. I reproduced all of it with plain HTTP against the same endpoint (https://opencode.ai/inference/go/openai/v1/chat/completions), with no opencode client in the loop, so this is server side.
Summary of what I measured (same account, same machine, warm prompt cache >= 95% so it is not a cold-prefill problem):
| input | n | median first token | p90 | max | > 20 s |
|---|---|---|---|---|---|
| tiny prompt (raw HTTP) | 120 | 1.6 s | 2.8 s | 28.5 s | 0.8% |
| 657k-token prompt (raw HTTP, back to back) | 34 | 4.3 s | 24.6 s | 59.0 s | 14.7% |
| app sessions, < 50k ctx (30 days) | 4764 | 3.4 s | 11.8 s | 283 s | 4.0% |
| app sessions, 150-300k ctx | 3327 | 7.6 s | 28.4 s | 239 s | 17.2% |
| app sessions, 300-500k ctx | 741 | 10.6 s | 37.4 s | 272 s | 28.5% |
| app sessions, 500k+ ctx | 142 | 16.8 s | 50.6 s | 126 s | 43.0% (> 60 s: 6.3%) |
Once the first token arrives the stream is fine (144 tok/s, max gap between chunks 0.27 s), and prefill is fast (a 1M-token cold prompt returns in 27 s), so the delay is specifically before the first token and it scales with prompt length.
Same endpoint, also reproducible:
- Streams that end with no
finish_reasonand no[DONE], which the client reports asprovider.invalid-output: OpenAI Chat stream ended without finish_reason(1 in 25 at 657k in my run; 15 hits in one session of the app yesterday). HTTP 200with a valid[DONE]but"choices": []andcompletion_tokens: 0- the model never runs and the client gets an empty turn.- Repeated identical prefix sometimes comes back with
cached_tokens: 0(full re-read) - same family as [URGENT] Go: frank/DeepSeek-V4.1-Flash upstream repeatedly drops prompt cache — ~50% of billed usage is full-context re-reads #50258. - The same 780 KB prompt was reported as
prompt_tokens: 160013(4.9 chars/token) on some calls and393392(2.0 chars/token) on another - that looks like two different backends/tokenizers answering the same request, and the empty completions came from the 160k-reporting one.
Ruled out on my side (each verified by measurement):
- network: caught a 21.8 s stall on an already-established pooled socket whose TCP state was
rtt:9ms retrans:0/.. pmtu:1500 cwnd:10, i.e. no loss, no MTU issue, remote just did not answer; - DNS: connecting to the Cloudflare IP directly (no DNS at all) changes nothing;
- Tailscale/AdGuard (DNS runs over the tailnet here): ran the same probe set with Tailscale fully stopped, no difference (median 1.83 s vs 1.76 s);
- concurrency: 16 parallel requests max 7.6 s;
- client/TUI: 120 sequential requests via raw HTTP are clean, and my
session_messagetimings match the raw HTTP numbers.
Plugins
Only a local one (rtk.ts, rewrites shell commands). No MCP servers.
OpenCode version
2.0.32
Steps to reproduce
- Take an
opencode-gokey. - Send a ~650k-token prompt ~40 times back to back (requests after the first are cache-warm):
curl -N https://opencode.ai/inference/go/openai/v1/chat/completions \
-H "Authorization: Bearer $OPENCODE_API_KEY" \
-H "Content-Type: application/json" \
-H "x-opencode-session: ses_probe_$(uuidgen)" \
-d '{"model":"deepseek-v4.1-flash","stream":true,"max_tokens":16,"messages":[{"role":"user","content":"<~650k tokens of text>\n\nReply with OK."}]}'- Measure time to the first token. Expect median ~4 s, p90 ~25 s, max ~60 s, ~15% of requests over 20 s. Occasionally the stream ends without
finish_reason/[DONE], and occasionally a200comes back withchoices: []andcompletion_tokens: 0. - In the app, the same effect shows up as the spinner sitting for minutes with no thinking/output; the rate grows with context size (see table above).
Related: #57040, #50258, #40479, #36889, and the stream ended without finish_reason reports (#56870, #56861, #56858, #50146).
Screenshot
Not applicable (raw numbers above; can provide logs/probe dumps on request).
Operating System
Ubuntu 26.04.1 LTS
Terminal
herdr 0.9.5 (TERM_PROGRAM=herdr), TUI runs inside it
Activity
opencode-agent commentedon Oct 19, 2026
Thanks for the detailed measurements and the raw-HTTP reproduction. I couldn't reproduce this here. Everything you describe happens on the OpenCode Go service itself, and testing it needs a real
opencode-gokey and very large prompts, which this sandbox doesn't have. That covers the first-token wait growing with context size, streams ending withoutfinish_reason/[DONE],200responses withchoices: []andcompletion_tokens: 0,cached_tokens: 0cache misses, and the differentprompt_tokenscounts for the same prompt. A maintainer with access to the Go gateway will need to look at it, and your probe dumps (with request timestamps and anyx-opencode-sessionIDs) will probably help them match requests in the server logs.Related issues, roughly from most to least related:
finish_reasonor[DONE]Ploppy3 commentedon Oct 19, 2026
Adding the request-level details you asked for. Everything below came from my own account; the machine is UTC+2 and the times are UTC, so you can align them with the Go logs directly.
Raw-HTTP probes:
POST https://opencode.ai/inference/go/openai/v1/chat/completions,model: deepseek-v4.1-flash,stream: true.req idis theidfield of the SSE chunks;sessionis the value I sent in thex-opencode-sessionheader.Three specific ones worth pulling from the list:
2026-10-19 10:44:29UTC, 59.0s to first token, 657894 prompt tokens of which 657792 cached - warm cache, still 59s to first token.2026-10-19 10:33:37UTC, 26.9s, 1037873 prompt tokens with cached: 0 - identical prefix served from cache one request earlier. Note this one (and the next) came back with agw-...id instead of the numeric one:021792406017716fd3ae808d5ba084b55a7f9e1600032cd3b9b17.HTTP 200,data: [DONE],"choices": [],completion_tokens: 0, sent withx-opencode-session: ses_probe_a5027c93e46b4525bd1bfe6665a6804f(around 10:48 UTC, 657k-token prompt, no id captured in my parser for it).From the app itself (same account), for the two failure modes:
For those 15, the stored
retryon the message is{"attempt": 2, "error": {"type": "provider.invalid-output", "message": "OpenAI Chat stream ended without finish_reason", "status": 200}}- so each truncation costs a second full request, and the retry is delivered as a synthetic "previous response was interrupted, continue" turn.I still have the raw probe dumps (per-chunk arrival timeline, response headers including
x-opencode-log-id) - happy to attach them wherever a maintainer wants them.Ploppy3 commentedon Oct 20, 2026
Adding controlled A/B results from the CLI to narrow this down. All runs use the same ~401k-token session/prefix, warm cache (401,280 cached tokens),
opencode-go/deepseek-v4.1-flash, effortmax, and a one-word output.Output-token cap is not the cause
Same prefix and cache. The gateway is not queueing on output-token reservation.
Explicit affinity headers are not the cause
x-session-affinityandX-Session-Id: still hit 401,280 cached tokens, TTFT 4.43s.x-opencode-sessiontoo: HTTP 400MissingSessionID— "Request is missing x-opencode-session and cannot be routed efficiently."So
x-session-affinity/X-Session-Idare redundant for Go;x-opencode-sessionis the actual routing key and cannot be removed.Same context, huge spread
In the same ~400k session, all with ~99.7% cache:
4.1, 7.1, 8.7, 11.2, 15.0, 15.7, 20.0, 48.8, 152.5, 176.6, 231.2, 236.1seconds.The 236s request had only 504 new tokens and 393,856 cached tokens. That rules out prefill, cache miss, decode speed, and max_tokens; it looks like endpoint/queue scheduling.
Session-age drift
Within-session median TTFT:
New sessions are fast mainly because they start at low context; a 401k fork can still be 4.2s warm.
Cross-check
Direct DeepSeek at 250–450k: p50 4.8s, p95 17.6s, max 42s.
Conclusion: this is not client request shape, max_tokens, or a missing affinity header.
x-opencode-sessionroutes to an endpoint/queue that occasionally takes minutes at long context. Can a maintainer check the gateway's endpoint selection/queue for that key, and exposex-opencode-endpoint-idagain so slow requests can be correlated with a specific upstream?