You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
GET /session/:id/message returns 400 when messages contain format with inline JSON schema #26929
The prompt is accepted (204), the LLM runs and completes successfully. But when calling GET /session/:id/message to retrieve messages, the server returns 400:
The format field matches the OutputFormatJsonSchema definition in the OpenAPI spec at GET /doc exactly (type, schema, retryCount with additionalProperties: false). The message listing endpoint's deserialization rejects what the prompt endpoint accepted.
This appears to be a regression in the May 10-11 2026 releases that tightened HTTP API validation. The same workflow worked on versions before May 10.
Plugins
None (using opencode serve as HTTP API server with MCP servers configured via opencode.json)
OpenCode version
1.14.48
Steps to reproduce
Steps to reproduce:
Start opencode serve --port 4096
Create a session: POST /session with {"title": "test"}
Send a prompt with format: POST /session/:id/prompt_async with body containing "format": {"type": "json_schema", "schema": {"type": "object", "properties": {"name": {"type": "string"}}, "required": ["name"]}, "retryCount": 2}
Wait for the LLM to complete (poll GET /session/status until not busy)
List messages: GET /session/:id/message
Observe 400 Bad Request with "Expected OutputFormatJsonSchema" error
Screenshot and/or share link
No response
Operating System
Linux (OpenShift container), also reproducible on macOS 25.4.0
Terminal
N/A (HTTP API usage via Python httpx client)
Reacted by Soare Robert Daniel, Rory, draedful, Trevor Wieland, Ion Cernei, Vladislav Stroev, Taylor Smith, rscambler and Joel
similar error for me
Happened when i opened a session created in an older opencode version, upgraded opencode to latest, and then opened the same session again. Using web ui (TUI shows no previous messages)
Something went wrong
An error occurred while loading the application.
Error Details
Error: Unknown error
at xf (.../assets/index-DiwOaO2o.js:2:10361)
at .../assets/index-DiwOaO2o.js:2:3660
────────────────────────────────────────
Caused by:
BadRequest
Expected OutputFormatJsonSchema, got {"type":"json_schema","schema": ......
1.14.41 is the last working version for me
Reacted by Soare Robert Daniel, Trevor Wieland, Cameron DeMille, Vladislav Stroev and Taylor Smith
On current dev, this OutputFormatJsonSchema codec area appears to have regressed further — from a 400 on read to a hang on write.
While adding a regression test that sends a structured prompt (format: { type: "json_schema", schema: { ... }, retryCount: 0 }), I found that persisting the user message never completes. The hang happens at message creation, before the model is ever called:
SessionPrompt.prompt
-> createUserMessage
-> sessions.updateMessage(info)
-> EventV2 publish of `message.updated` (durable)
-> commitSyncEvent
-> Schema.encodeUnknownSync(<message.updated data schema>)(event.data) // does not terminate
Schema.encodeUnknownSync of the durable message.updated event data does not terminate when info.format is an OutputFormatJsonSchema. Non-structured messages (no format) encode and persist normally on the exact same path; only format: json_schema triggers it. It reproduces deterministically (observed via a 3s test timeout in packages/opencodetest/session/prompt.test.ts — the encode call never returns), with [email protected].
This looks like the same root cause as the read-side 400 reported here (the OutputFormatJsonSchema / inline-JSON-schema codec), now also surfacing on the encode/persist side as a non-terminating encode rather than a validation error.
I just wanted to add another reproduction case to this thread.
I'm using the Node.js SDK and calling client.session.promptAsync() with a format key containing a JSON schema to force structured JSON output. When I do this:
client.session.messages contains no text messages after the call completes
No SSE events fire that carry text message parts — the stream appears to go silent on the text side
Removing the format key entirely restores normal behaviour — messages populate and SSE events fire as expected — but the final response is text. So it's definitely isolated to the format: json_schema code path, consistent with what's described here.
It's still happening as of v1.17.7. Any update on a fix or workaround would be much appreciated.
Using the same format with synchronous session.prompt() works and returns info.structured.
So it looks like the async prompt accepts the format, but the message listing endpoint later fails on it. This blocks SDK clients that use promptAsync + session.messages() with native structured output.
The message with the bad format is not persisted to the DB (no format key in any message row), so the 400 comes from the server's read-back/encode path - consistent with the Schema.Class vs plain-object diagnosis.
In the Desktop UI this means every session load fails with 400 while such a message exists in server memory - not just API clients.
PR #37541 already contains the fix (wire schemas changed from Schema.Class to Schema.Struct) with the regression test passing and a verified Windows build - but it has sat unreviewed/unmerged since Jul 17. People are actively hitting this. Can we get it reviewed and merged?
Confirming in 1.18.x — and the blast radius is much larger than "a 400 on message GET"
Reproducing this on opencode 1.18.x (server) today, but via the most common real‑world trigger: the opencode-claude-memory plugin, whose recall-selector agent sends exactly this format on every session.prompt:
The prompt is accepted, the LLM completes, but the server then rejects the persisted message on read‑back and retries the whole structured‑output call in a tight loop — indefinitely:
level=WARN message="schema rejection" kind=Body
reason="Expected OutputFormatJsonSchema, got {\"type\":\"json_schema\",\"schema\":{...},\"retryCount\":2}\n at [0][\"info\"][\"format\"]"
Why this matters beyond a 400
It's a runaway retry loop, not a one‑off rejection. Each rejection retries every ~5 s, 24/7, for the life of the session. I measured >1,600 schema rejection lines/hour sustained over days from a single recall sub‑session — i.e. thousands of wasted LLM calls + a session that never goes idle. The original report stops at "GET returns 400"; the actual user‑visible behaviour is an infinite retry storm.
It breaks the most popular memory plugin for everyone. Any opencode install with opencode-claude-memory enabled hits this on every turn — recall is non‑fatal to the main conversation, but it silently burns quota and stalls the session. The plugin conforms to the SDK's OutputFormatJsonSchema exactly; the server's validator is the one out of sync.
It cascades into downstream integrations. I'm seeing this via kimaki (a Discord bridge over opencode): each stuck recall sub‑session gets mirrored into Discord as a Sync: thread, producing hundreds of spam threads across every channel + a bot stuck "typing…" forever. That downstream damage is what surfaced the bug for us — but it's a symptom of this same root cause.
Expected vs actual
Expected: a format that the prompt endpoint accepts (and that matches GET /doc's OutputFormatJsonSchema) round‑trips through GET /session/:id/message unchanged.
Actual: the message‑listing deserialiser rejects its own persisted shape (Expected OutputFormatJsonSchema, got {"type":"json_schema",...}) and the caller retries forever.
Environment
opencode 1.18.x (server, via kimaki → opencode SDK), Windows + the in‑process plugin path.
Triggering client: @opencode-ai/sdksession.prompt with format: { type: "json_schema", ... } (the recall‑selector pattern).
Fix
#37541 addresses exactly this — it changes the OutputFormat* schemas in packages/schema/src/v1/session.ts so persisted formats are encoded with a stable identifier and round‑trip cleanly, and adds a returns persisted output formats test covering the json_schema case. From reading the diff it should close both the HTTP‑API 400 and the in‑process retry loop reported here (same root cause: the persisted format losing its schema identifier on read‑back). Could this get some review eyes? The retry‑loop + quota‑burn angle makes it higher priority than the issue title suggests — every opencode-claude-memory user is affected.
Activity
JoAMD commentedon May 23, 2026
similar error for me
Happened when i opened a session created in an older opencode version, upgraded opencode to latest, and then opened the same session again. Using web ui (TUI shows no previous messages)
Something went wrong
An error occurred while loading the application.
Error Details
1.14.41 is the last working version for me
ririnto commentedon Jun 4, 2026
On current
dev, thisOutputFormatJsonSchemacodec area appears to have regressed further — from a 400 on read to a hang on write.While adding a regression test that sends a structured prompt (
format: { type: "json_schema", schema: { ... }, retryCount: 0 }), I found that persisting the user message never completes. The hang happens at message creation, before the model is ever called:Schema.encodeUnknownSyncof the durablemessage.updatedevent data does not terminate wheninfo.formatis anOutputFormatJsonSchema. Non-structured messages (noformat) encode and persist normally on the exact same path; onlyformat: json_schematriggers it. It reproduces deterministically (observed via a 3s test timeout inpackages/opencodetest/session/prompt.test.ts— theencodecall never returns), with[email protected].This looks like the same root cause as the read-side 400 reported here (the
OutputFormatJsonSchema/ inline-JSON-schema codec), now also surfacing on the encode/persist side as a non-terminating encode rather than a validation error.akoukoullis commentedon Jun 17, 2026
I just wanted to add another reproduction case to this thread.
I'm using the Node.js SDK and calling
client.session.promptAsync()with aformatkey containing a JSON schema to force structured JSON output. When I do this:client.session.messagescontains no text messages after the call completesRemoving the
formatkey entirely restores normal behaviour — messages populate and SSE events fire as expected — but the final response is text. So it's definitely isolated to theformat: json_schemacode path, consistent with what's described here.It's still happening as of v1.17.7. Any update on a fix or workaround would be much appreciated.
fix(session): respect format.retryCount and fix OutputFormat encoding
sud0why commentedon Jul 2, 2026
I can reproduce this with matching versions:
opencode: 1.17.13@opencode-ai/sdk: 1.17.13Using the SDK:
promptAsyncsucceeds with HTTP 204, butsession.messages()returns HTTP 400:Using the same
formatwith synchronoussession.prompt()works and returnsinfo.structured.So it looks like the async prompt accepts the format, but the message listing endpoint later fails on it. This blocks SDK clients that use
promptAsync+session.messages()with native structured output.Hope this can be fixed soon. Thanks!
evilhikari commentedon Aug 9, 2026
Still reproduces on 1.18.15 (Desktop app on Windows) - months after this was reported.
Additional data points from our debugging:
PR #37541 already contains the fix (wire schemas changed from Schema.Class to Schema.Struct) with the regression test passing and a verified Windows build - but it has sat unreviewed/unmerged since Jul 17. People are actively hitting this. Can we get it reviewed and merged?
Futuri-Risk commentedon Aug 12, 2026
Confirming in 1.18.x — and the blast radius is much larger than "a 400 on message GET"
Reproducing this on opencode 1.18.x (server) today, but via the most common real‑world trigger: the
opencode-claude-memoryplugin, whoserecall-selectoragent sends exactly thisformaton everysession.prompt:{ "type": "json_schema", "schema": { "type": "object", "properties": { "selected_memories": { "type": "array", "items": { "type": "string" } } }, "required": ["selected_memories"], "additionalProperties": false }, "retryCount": 2 }The prompt is accepted, the LLM completes, but the server then rejects the persisted message on read‑back and retries the whole structured‑output call in a tight loop — indefinitely:
Why this matters beyond a 400
schema rejectionlines/hour sustained over days from a single recall sub‑session — i.e. thousands of wasted LLM calls + a session that never goes idle. The original report stops at "GET returns 400"; the actual user‑visible behaviour is an infinite retry storm.opencode-claude-memoryenabled hits this on every turn — recall is non‑fatal to the main conversation, but it silently burns quota and stalls the session. The plugin conforms to the SDK'sOutputFormatJsonSchemaexactly; the server's validator is the one out of sync.kimaki(a Discord bridge over opencode): each stuck recall sub‑session gets mirrored into Discord as aSync:thread, producing hundreds of spam threads across every channel + a bot stuck "typing…" forever. That downstream damage is what surfaced the bug for us — but it's a symptom of this same root cause.Expected vs actual
formatthat the prompt endpoint accepts (and that matchesGET /doc'sOutputFormatJsonSchema) round‑trips throughGET /session/:id/messageunchanged.Expected OutputFormatJsonSchema, got {"type":"json_schema",...}) and the caller retries forever.Environment
1.18.x(server, viakimaki→ opencode SDK), Windows + the in‑process plugin path.@opencode-ai/sdksession.promptwithformat: { type: "json_schema", ... }(the recall‑selector pattern).Fix
#37541 addresses exactly this — it changes the
OutputFormat*schemas inpackages/schema/src/v1/session.tsso persisted formats are encoded with a stable identifier and round‑trip cleanly, and adds areturns persisted output formatstest covering thejson_schemacase. From reading the diff it should close both the HTTP‑API 400 and the in‑process retry loop reported here (same root cause: the persisted format losing its schema identifier on read‑back). Could this get some review eyes? The retry‑loop + quota‑burn angle makes it higher priority than the issue title suggests — everyopencode-claude-memoryuser is affected.fix: keep OpenCode turns alive after rejected tools (#15)
docs(adapters): OpenCode format read defect re-checked: genuine, know…
thdxr commentedon Oct 11, 2026
Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (
opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.— from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊