Skip to content

[Bug] SQLite UPSERT into part table fails during step-finish event projection #31990

Description

@irfndi

Description

OpenCode crashed while persisting a step-finish event to the part table.

Failing SQL

insert into "part" ("id", "message_id", "session_id", "time_created", "time_updated", "data")
values (?, ?, ?, ?, ?, ?)
on conflict ("part"."id")
do update set "time_updated" = ?, "data" = ?

Params from the failure

placeholder value
id prt_eb9e9419c00129b4Qayl09Jh29
message_id msg_eb9e8af5a001VQwRWtmki2gBkj
session_id ses_14f0f5449ffehYzJNptQTs6Jnl
time_created 1781235532189
time_updated 1781235532193
data {"reason":"tool-calls","snapshot":"8a393ddff176c8c0f2d8149f9074495b7d5dbd1b","type":"step-finish","tokens":{"total":404062,"input":399574,"output":170,"reasoning":492,"cache":{"write":0,"read":3826}},"cost":0.24179232}

Where it comes from

The query is generated in the SessionV1.Event.PartUpdated projector:

https://github.com/anomalyco/opencode/blob/dev/packages/core/src/session/projector.ts#L281-L292

yield* db
  .insert(PartTable)
  .values({ id, message_id: messageID, session_id: sessionID, time_created: event.data.time, data })
  .onConflictDoUpdate({ target: PartTable.id, set: { data } })
  .run()
  .pipe(Effect.orDie)

The step-finish payload is produced in packages/opencode/src/session/processor.ts and published via session.updatePart(...).

Likely problem

Drizzle ORM table-qualifies the conflict target as "part"."id" instead of the bare "id". SQLite's UPSERT conflict target must match the PRIMARY KEY / UNIQUE constraint exactly. The part table defines id as the primary key, so the valid target is ON CONFLICT ("id"). Depending on the SQLite / better-sqlite3 build, ON CONFLICT ("part"."id") can fail with a constraint-mismatch error.

Other possible causes: oversized data payload, a NOT NULL violation on time_updated/data during the update, or SQLite locking / corruption.

What would help diagnose

-- SQLite version used by the running binary
SELECT sqlite_version();

-- confirm the part table primary key
PRAGMA table_info(part);

-- check for oversized / recent part rows
SELECT id, length(data) AS data_len FROM part ORDER BY time_created DESC LIMIT 5;

Also, the full error message / stack trace from the crash would confirm whether this is the Drizzle table-qualified conflict-target issue or something else.

Related issues

Possibly related to the same persistence path: #31204, #23673, #28989, #18921.

Plugins

Oh my openagent 4.8.1

OpenCode version: 1.17.3

Steps to reproduce

  1. Start or continue a session.
  2. Trigger a step that finishes with tool calls and snapshot data.
  3. The projector attempts to upsert the step-finish part row.
  4. The query fails.

Screenshot and/or share link

Image

Operating System

MacOS 26.1

Terminal

Ghosty/Zed

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions