anomalyco / anomalyco/opencode
session/fork and GET /session/:id/message fail for sessions containing a message with an output format
@jlongster is already working on this.
Since Aug 2, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
Description
Any message persisted with an info.format value cannot be re-emitted through the schema layer: Format is a union of Schema.Class (packages/schema/src/v1/session.ts:65-79), but what comes back out of storage is a plain object, so encoding fails.
Two symptoms on 1.17.13:
1. Listing messages returns 400. For any session where a prompt used format:
BadRequest: Expected OutputFormatJsonSchema, got {"type":"json_schema","schema":{...},"retryCount":5}
at [0]["info"]["format"]
The route declares success: Schema.Array(SessionV1.WithParts) (packages/opencode/src/server/routes/instance/httpapi/groups/session.ts:179) and the stored row is encoded against it as-is.
2. Fork copies no messages. fork re-publishes each copied message through updateMessage -> events.publish(SessionV1.Event.MessageUpdated, { info }) (packages/opencode/src/session/session.ts:713 and :631), which hits the same encode and dies on the first format-carrying message. The result is a created-but-empty session: 0 messages, all tokens zero, time.created == time.updated. The request does not complete, so an HTTP client that retries on timeout/5xx keeps creating empty sessions - a single fork call left us with 35 orphaned shells before we noticed.
Prompting an existing session is not affected: model context is read from the DB directly and only the new message gets encoded. So a session can be permanently unlistable and unforkable while still working fine for chat, which is how this went unnoticed for a while on our side.
Plugins
none
OpenCode version
1.17.13
Steps to reproduce
- Create a session.
- Send a prompt with an output format, e.g.
POST /session/:id/messagewith"format": {"type": "json_schema", "schema": {"type": "object", "additionalProperties": false, "properties": {"answer": {"type": "string"}}, "required": ["answer"]}}. This succeeds and returns the assistant message normally. GET /session/:id/messageon that session -> 400 BadRequest,Expected OutputFormatJsonSchema ... at [0]["info"]["format"].POST /session/:id/forkon that session -> a new session appears in the session list, but it has no messages and zero tokens.
Same result with "format": {"type": "text"} in step 2, which reports Expected OutputFormatText, got {"type":"text"}.
Sessions with no formatted prompt in their history list and fork normally, so the trigger is specifically a stored info.format.
Screenshot and/or share link
No response
Operating System
Linux (opencode running in server mode)
Terminal
n/a - driven over the HTTP API, not the TUI
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.