Repository navigation
Description
Summary
A tool call that is interrupted mid-stream by a provider transport error (Decode error (200 POST <redacted-proxy-url>/v1/chat/completions)) is persisted into session history in a shape that fails SessionModelRequest.prepare schema validation. Every subsequent message in that session fails with Session failed / Schema validation failed — the session becomes permanently unusable (even after switching models, since validation happens before provider selection).
Environment
- opencode version: 2.0.15
- OS: Linux 6.6.87.2-microsoft-standard-WSL2 (x86_64)
- Terminal: TERM=xterm-256color
- Shell: /bin/bash
- Install/channel: latest (release)
- Active plugins:
@cortexkit/opencode-magic-context@latest - Provider:
@ai-sdk/openai-compatibleagainst a self-hosted LiteLLM proxy (URL redacted), modelfree_glm-5.3-flash(OpenAI-compatible chat completions)
Reproduction
- Configure an OpenAI-compatible provider (LiteLLM proxy in my case).
- Run a session where the model emits a tool call and the provider returns a response body that fails client-side decoding mid-stream (observed with GLM 5.3 Flash via LiteLLM: HTTP 200 with an undecodable body during a
shelltool call). - The interrupted turn is persisted. In the storage record the tool item looks like this (note
input: {}, nocontent,finish: "error"on the message):{ "type": "tool", "id": "chatcmpl-tool-…", "name": "shell", "executed": false, "state": { "status": "error", "input": {}, "error": { "type": "provider.transport", "message": "Decode error (200 POST https://<redacted-proxy-url>/v1/chat/completions)", "status": 200 } } } - Send any follow-up message (or "continue") in the same session.
Expected Behavior
The failed tool call should be sanitized into a valid history item (e.g. a tool-result with type: "error"), and the session should continue working.
Actual Behavior
Every drain of the session fails with:
ERROR Failed to drain Session cause="Error: Schema validation failed
at SessionModelRequest.prepare (...)
at SessionRunner.runStep (...)
at SessionRunner.drain (...)
The embedded Effect Schema issue points at the tool item: Pointer path=["content"] → Pointer path=[3] → AnyOf over LLM.Content.* (Text/Media/ToolCall/ToolResult/Reasoning/Compaction/Effort) with zero matching members — i.e. the persisted shape of the interrupted tool item matches no allowed content variant.
Observed 12+ consecutive failures, all for the same session:
timestamp=… level=ERROR message="Failed to drain Session" sessionID=ses_… cause="Error: Schema validation failed"
After the first failure, every new user message in that session immediately ends with type: "idle", outcome: "failed" — no assistant message is created. Switching models does not help (validation runs before the provider is contacted). Other sessions in the same project with normal tool errors validate fine, which isolates the problem to the provider.transport decode-error tool item shape:
- tool item with
status: "error", errortype: "tool.execution"(permission rejection) → drains OK - tool item with
status: "error", errortype: "aborted"→ drains OK - tool item with
status: "error", errortype: "unknown"(tool cancelled) → drains OK - tool item with
status: "error", errortype: "provider.transport"(decode error mid tool-call, emptyinput) → permanently poisons the session
Related Issues
- Failed to drain Session: one malformed tool result wedges the whole session #50775 — same symptom class (a single malformed tool result permanently wedges the session at
prepare/drain), but a different failure mechanism: that report hits aTypeError(s.result.value.map is not a function) insideunsupportedParts, while this report fails earlier at Effect Schema validation of the persistedLLM.Contentunion. The sanitization approach from PR fix(core): degrade malformed tool-result content instead of crashing prepare #50776 does not cover this path, since the malformed item never decodes into a validLLM.Contentvariant in the first place. - Session permanently frozen: drain() failures are logged but never surfaced to the client (no session.error event) #49740 — drain() failures are logged but never surfaced to the client; explains why affected sessions appear silently wedged from the UI side.
- ai: preserve diagnostics for mid-stream HTTP decode failures #42482 — the trigger side: mid-stream HTTP decode failures are collapsed into a generic
Decode error (200 POST …), losing the underlying cause.
Additional Context
- The trigger on the provider side was a single malformed response from a LiteLLM proxy (GLM 5.3 Flash, OpenAI-compatible endpoint). One bad response permanently bricks the session.
- Only workaround found: abandon the session and start a new one; the broken session remains readable but cannot accept messages. Model switching and service restart do not help.
- Suggested fix directions: sanitize orphaned/failed tool calls in
SessionModelRequest.prepare(map error-state tool items to a validLLM.Content.ToolResultwithtype: "error"), and/or validate/sanitize the persisted message when it is written, not on every subsequent drain. This complements the fix in PR fix(core): degrade malformed tool-result content instead of crashing prepare #50776, which only guards the in-memory.mappath. - Logs available on request (redacted). Storage record and full Effect Schema issue dump were inspected locally; happy to attach them.
Activity
opencode-agent commentedon Oct 22, 2026
Closing as won't fix. This is caused by the
@cortexkit/opencode-magic-contextplugin, not by OpenCode core.The plugin registers a V2
session.hook("context")that rewrites the outgoing messages. When it restores folded history (restoreRow), it builds errored tool results by hand as{ type: "tool-result", ..., result: { error, content }, resultType: "error" }. That is not a validLLM.ToolResultbecauseresulthas notype, so building the request fails withSchema validation failedoncontent → [n] → AnyOf. That matches the trace in this report. Rows outside the plugin's restored range go through OpenCode's own lowering and are fine, which is why other errored tools appeared to work.What we checked:
shelltool withinput: {}and aprovider.transporterror) gives a valid tool-call and error tool-result pair on both v2.0.15 and currentdev. The tool-error lowering into-llm-message.tshasn't changed since v2.0.15.Please report this to the plugin's maintainers. Disabling the plugin, or starting a new session, avoids it.