Cerebras qwen-3.8-27b: infinite retry loop — history replay sends reasoning_content, Cerebras rejects it with 400
Summary
On the desktop app (v1.18.30), any session using cerebras/qwen-3.8-27b where the assistant produces reasoning hangs permanently: the UI spins forever and never shows an error. Under the hood OpenCode retries the same failing request in an infinite loop.
Root cause
qwen-3.8-27b emits a reasoning part for its reply. On the next request, OpenCode replays the assistant message including its reasoning, and the chat-completions transport serializes it as a plain-text reasoning_content field. Cerebras rejects that field:
400 {"message":"messages.1.assistant.reasoning_content: property 'messages.1.assistant.reasoning_content' is unsupported","type":"invalid_request_error","param":"validation_error","code":"wrong_api_format"}
The retry logic treats the failure as transient and resends the identical request, which fails identically — forever. The user sees an unresponsive session with no surfaced error.
The replay path: stored reasoning parts → toModelMessages (session/message-v2.ts) → ProviderTransform.message (provider/transform.ts) → SDK converter emits reasoning_content. The existing interleaved branch in normalizeMessages only reroutes reasoning when the model declares interleaved.field in models.dev; Cerebras models declare nothing, so the parts fall through to the SDK's plain-text echo.
Evidence
Captured request from the desktop app (via logging proxy in front of api.cerebras.ai) — the looping request contains the assistant's reasoning verbatim:
{
"model": "qwen-3.8-27b",
"messages": [
{"role": "system", "content": "You are a helpful AI assistant tasked with summarizing conversations.…"},
{"role": "user", "content": "Remember the word pineapple. Reply with just: OK"},
{"role": "assistant", "content": "\n\nOK",
"reasoning_content": "The user is asking me to remember the word \"pineapple\" and to reply with just \"OK\"."},
{"role": "user", "content": "…"}
],
"stream": true
}
Turn 1 (no reasoning in history) → 200. Every request replaying the reasoning → 400, retried indefinitely.
Live API probes against api.cerebras.ai (both current Cerebras chat models):
| request history contains |
qwen-3.8-27b |
gpt-oss-120b |
assistant.reasoning_content |
400 reasoning_content … is unsupported |
400 |
assistant.reasoning |
200 |
200 |
| no reasoning fields |
200 |
200 |
So Cerebras rejects reasoning_content outright, for every model. The config is otherwise completely default (no custom agents/prompts; the session hangs on a trivial two-turn conversation).
Environment
- OpenCode desktop v1.18.30 (macOS), provider
cerebras, model qwen-3.8-27b
- Clean default config — no custom provider overrides
Proposed fix
Strip reasoning parts from outbound assistant history for chat-completions transports that echo reasoning as plain-text body fields (@ai-sdk/cerebras, @ai-sdk/openai-compatible, @ai-sdk/deepinfra, @ai-sdk/togetherai, @ai-sdk/deepseek, ai-gateway-provider, venice-ai-sdk-provider) unless the model declares interleaved reasoning support in models.dev (e.g. DeepSeek, which explicitly accepts it). Implemented in PR linked below; validated end-to-end against the live Cerebras API (two-turn + history replay, no reasoning_content on the wire, no loop).
Cerebras qwen-3.8-27b: infinite retry loop — history replay sends
reasoning_content, Cerebras rejects it with 400Summary
On the desktop app (v1.18.30), any session using
cerebras/qwen-3.8-27bwhere the assistant produces reasoning hangs permanently: the UI spins forever and never shows an error. Under the hood OpenCode retries the same failing request in an infinite loop.Root cause
qwen-3.8-27b emits a reasoning part for its reply. On the next request, OpenCode replays the assistant message including its reasoning, and the chat-completions transport serializes it as a plain-text
reasoning_contentfield. Cerebras rejects that field:The retry logic treats the failure as transient and resends the identical request, which fails identically — forever. The user sees an unresponsive session with no surfaced error.
The replay path: stored reasoning parts →
toModelMessages(session/message-v2.ts) →ProviderTransform.message(provider/transform.ts) → SDK converter emitsreasoning_content. The existinginterleavedbranch innormalizeMessagesonly reroutes reasoning when the model declaresinterleaved.fieldin models.dev; Cerebras models declare nothing, so the parts fall through to the SDK's plain-text echo.Evidence
Captured request from the desktop app (via logging proxy in front of
api.cerebras.ai) — the looping request contains the assistant's reasoning verbatim:{ "model": "qwen-3.8-27b", "messages": [ {"role": "system", "content": "You are a helpful AI assistant tasked with summarizing conversations.…"}, {"role": "user", "content": "Remember the word pineapple. Reply with just: OK"}, {"role": "assistant", "content": "\n\nOK", "reasoning_content": "The user is asking me to remember the word \"pineapple\" and to reply with just \"OK\"."}, {"role": "user", "content": "…"} ], "stream": true }Turn 1 (no reasoning in history) → 200. Every request replaying the reasoning → 400, retried indefinitely.
Live API probes against
api.cerebras.ai(both current Cerebras chat models):qwen-3.8-27bgpt-oss-120bassistant.reasoning_contentreasoning_content … is unsupportedassistant.reasoningSo Cerebras rejects
reasoning_contentoutright, for every model. The config is otherwise completely default (no custom agents/prompts; the session hangs on a trivial two-turn conversation).Environment
cerebras, modelqwen-3.8-27bProposed fix
Strip reasoning parts from outbound assistant history for chat-completions transports that echo reasoning as plain-text body fields (
@ai-sdk/cerebras,@ai-sdk/openai-compatible,@ai-sdk/deepinfra,@ai-sdk/togetherai,@ai-sdk/deepseek,ai-gateway-provider,venice-ai-sdk-provider) unless the model declaresinterleavedreasoning support in models.dev (e.g. DeepSeek, which explicitly accepts it). Implemented in PR linked below; validated end-to-end against the live Cerebras API (two-turn + history replay, noreasoning_contenton the wire, no loop).