Empty-body 400 from OpenAI-compatible gateways (opencode-go/deepseek-v4-flash) misclassified as context overflow → destructive auto-compaction destroys up to ~400k tokens
Severity
This is a serious, non-cosmetic bug that directly degrades pi's quality of work. A transient HTTP 400 with an empty body from an OpenAI-compatible gateway is misclassified as context overflow, which makes pi skip retry and destroys up to ~400k tokens of conversation history via auto-compaction — even though context usage is only 13–40% of the model's window and the error message carries zero context/token semantics. Sessions lose exact constraints, file paths, decisions, and in-progress work, forcing the model to continue from a summary.
Summary
isContextOverflow() classifies 400 status code (no body) as context overflow via the generic Cerebras regex:
/^4(?:00|13)\s*(?:status code)?\s*\(no body\)/i, // Cerebras: 400/413 with no body
This pattern was written for Cerebras backends (which genuinely report overflow as empty-body 400/413), but it matches any OpenAI-compatible gateway that returns an empty-body 400. On the built-in opencode-go provider (DeepSeek V4 Flash) the gateway intermittently returns exactly such a response. Result: zero retry + destructive overflow auto-compaction (_runAutoCompaction("overflow")).
Environment
- pi 0.84.4 (also reproduced by user @ShoichiTect on 0.85.1 in #8682)
- api=
openai-completions, provider=opencode-go(baseUrlhttps://opencode.ai/zen/go/v1), model=deepseek-v4-flash,contextWindow= 1,000,000,maxTokens= 384,000 - No custom compaction settings → defaults apply:
reserveTokens= 16,384 → real threshold = 983,616 tokens
Evidence (from a session log)
Two occurrences in one session, provider opencode-go / deepseek-v4-flash:
| t | preceding request usage (input + cacheRead) | compaction tokensBefore |
|---|---|---|
| 2026-09-11T10:19:01Z error | 396,823 | 397,619 |
| 2026-09-11T10:26:49Z error | 126,294 | 127,210 |
- Both compactions are immediately preceded by an assistant message with
stopReason: "error",errorMessage: "400 status code (no body)", and all-zero usage (the request was rejected before any usage was computed). - Real overflow threshold is 983,616; the failing requests were at ~40% and ~13% of the 1M window. Neither was anywhere near overflow.
- The 400 fires at wildly different sizes (≈397k and ≈127k) — not a genuine token-limit rejection. Requests of the same size succeeded dozens of times before/after in the same session.
- The OpenAI SDK surfaces the message because the response body is empty:
openai/core/error.mjsmakeMessage()→status && !msg → "${status} status code (no body)". Nothing about context is in the text.
Why this is a real bug
- An error whose message contains no context/token wording must never land on the terminal overflow path. The classification rests solely on the Cerebras regex matching by accident.
- Inconsistent error handling: the same gateway's empty-body 429/5xx map to retryable transient errors (backoff, retry) — but empty-body 400/413 take the zero-retry destructive compaction path. A transient gateway glitch should be retried, not compacted.
- Impact is severe: losing 100–400k tokens mid-task breaks pi's constraints, file paths, partial work, and cross-turn coherence — i.e. it materially degrades the quality of affected sessions.
Proposed fix
- Gate the bodyless pattern on
provider === "cerebras"(as suggested in #8682), or remove bodyless 400 fromOVERFLOW_PATTERNSentirely — keep bodyless 413 gated to Cerebras (mirrors openclaw/openclaw#119596 which removed only the 400). - Add empty-body 4xx to
RETRYABLE_PROVIDER_ERROR_PATTERNso transient gateway 400s get bounded backoff retry instead of compaction (consistent with 429/5xx handling).
Related
- #8682 — same issue, auto-closed by the bot before maintainer review despite a reproduction + fix proposal on 0.85.1 (compaction
tokensBefore41,682 / 108,534 / 157,310 at threshold 983,616). - #8526 — false overflow from the same Cerebras regex for Vertex AI.
- #5763 — gateway error bodies are swallowed in general (root cause of opaque
"(no body)"strings). - deepseek-ai/deepseek-harness discussion #2865 — same mislabeling for other non-Cerebras OpenAI-compatible providers.
- openclaw/openclaw#119596 — analogous fix (removed bodyless 400 from overflow patterns).
This is reproducible with the stock opencode-go provider and an OpenCode Go key — no custom provider or config needed.
Source: earendil-works/pi