anomalyco / anomalyco/opencode

session/fork and GET /session/:id/message fail for sessions containing a message with an output format

Open
#40,169 1 comment 0 reactions 1 assignee View on GitHub

@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
  1. Create a session.
  2. Send a prompt with an output format, e.g. POST /session/:id/message with "format": {"type": "json_schema", "schema": {"type": "object", "additionalProperties": false, "properties": {"answer": {"type": "string"}}, "required": ["answer"]}}. This succeeds and returns the assistant message normally.
  3. GET /session/:id/message on that session -> 400 BadRequest, Expected OutputFormatJsonSchema ... at [0]["info"]["format"].
  4. POST /session/:id/fork on 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.