Repository navigation
Description
Summary
When two opencode processes (a legacy opencode-ai V1 process and the new @opencode/cli V2 service) run concurrently against the same SQLite database, both write the per-aggregate event stream with an unguarded sequence counter. They allocate the same seq, producing SQLiteError: UNIQUE constraint failed: event.aggregate_id, event.seq. The failure is not self-healed: event_sequence.seq is left behind MAX(event.seq), so any later event write for that session collides again. The store never reconciles the watermark, and the colliding writer surfaces an unhandled EffectDrizzleQueryError instead of retrying or taking ownership.
Environment
- opencode version:
opencode v2.0.26 - OS: Windows 10.0.19045.7725 (win32 x64)
- Terminal: Windows Terminal (
WT_SESSIONset),TERM=xterm-256color - Shell:
C:\WINDOWS\system32\cmd.exe - Install/channel: npm global
@opencode/cli@2.0.26(release). Legacyopencode-aiwas also installed and running during the incident. - Active plugins: none configured (
opencode.jsoncdefines noplugins, and no.opencode/plugin(s)/directory exists). Note:@opencode-ai/plugin@1.18.31(V1 SDK) is present inpackage.jsonbut V1 plugins do not load on V2.
Reproduction
- Have a legacy
opencode-ai(V1) process running a session, using the shared data dir~/.local/share/opencode. - Start opencode V2 (
@opencode/cli) against the same data dir; the V2 CLI starts a service (opencode serve --service) that runs migrations and begins writing events for the same session. - Let both processes write events for the same session aggregate (e.g. continue the session in V1 while V2 is active).
- Observe repeated
UNIQUE constraint failed: event.aggregate_id, event.seqin~/.local/share/opencode/log/opencode.logwhileevent_sequence.seq < MAX(event.seq).
Expected Behavior
- A second writer should either acquire the per-aggregate owner/lease (
event_sequence.owner_id) or fail fast with a clear, handled error instead of an unhandled Drizzle error. - The event store should keep
event_sequence.seqconsistent withMAX(event.seq)— i.e. self-heal or reconcile on load so a session is never permanently unable to write events. - Running two opencode instances (or versions) against one data dir should be detected/prevented, or documented as unsupported.
Actual Behavior
Event inserts reuse an already-taken seq and fail. Three real occurrences on 2026-10-11 (all in the same server run run=0e1b403f):
timestamp=2026-10-11T05:09:44.400Z level=ERROR ... error="EffectDrizzleQueryError: Failed query: insert into \"event\" (\"id\", \"aggregate_id\", \"seq\", \"type\", \"data\") values (?, ?, ?, ?, ?)
params: evt_1295def4a0013KOeqOSu6zncqN,ses_ed7375da6ffeXS4BoGpzP5CiTY,1729,message.part.updated.1,...
(cause: ... SQLiteError: UNIQUE constraint failed: event.aggregate_id, event.seq)"
timestamp=2026-10-11T05:11:14.244Z level=ERROR ... seq=1729 message.updated.1 (same UNIQUE failure)
timestamp=2026-10-11T05:11:25.174Z level=ERROR ... seq=1729 message.updated.1 (same UNIQUE failure)
Resulting inconsistent state for aggregate ses_ed7375da6ffeXS4BoGpzP5CiTY:
event_sequence.seq = 1728
MAX(event.seq) = 1772 -- 1623 rows, seq 150..1772, no gaps, no duplicate rows
So the next write computes 1729, which already exists, and collides. The watermark never caught up on its own; the only affected aggregate was this session (event_sequence had 153 rows; exactly one had seq < MAX(event.seq)). event_sequence.owner_id was NULL for every row, i.e. the single-writer guard was never engaged.
Additional Context
- Timeline from the log (run ids = distinct server/process instances):
run=0e1b403f: first log 2026-10-11T02:26:22Z, last log 2026-10-11T05:11:25Z — this is the legacyopencode-aiprocess; its final log line is the third UNIQUE error, after which it exited.run=a2c3cf22andrun=89563b95: started 2026-10-11T04:25:27Z (the V2 CLI +serve --service).- Both overlapped, so two writers hit the same
eventtable.
- Because the session's durable event log froze while the watermark stayed at 1728, the session's
time_updatedstopped at the last successful write and no events were persisted afterwards. - Workaround applied locally (server left running):
UPDATE event_sequence SET seq = (SELECT MAX(seq) FROM event WHERE aggregate_id = event_sequence.aggregate_id) WHERE aggregate_id = 'ses_ed7375da6ffeXS4BoGpzP5CiTY';→ watermark 1728 → 1772, zero aggregates withwatermark < event_max, no new UNIQUE errors. - Frequency: occurred exactly 3 times, only while two processes shared the DB; it is intermittent and depends on concurrent writes, which makes it easy to miss but leave the store permanently broken for the affected aggregate.
- The
owner_idcolumn (added by migration20260504145000_add_sync_owner) looks like the intended single-writer mechanism, but it wasNULL, so ownership was never claimed. Consider claiming/validating ownership before allocatingseq, and reconcilingevent_sequence.seqwithMAX(event.seq)on startup. - Redactions: no secrets included; session/event ids are opaque.
Activity
opencode-agent commentedon Oct 11, 2026
Thanks for the detailed report, the DB state and the workaround query. I think this is a duplicate of #51411. That issue covers the same failure:
event_sequence.seqfalls behindMAX(event.seq), and from then on every event write for that session fails withUNIQUE constraint failed: event.aggregate_id, event.seq. It also names a v1 and v2 process sharing one data directory as a trigger. The current code still takes the next seq fromevent_sequencealone, with no step that brings it back in line with theeventtable.Possible duplicates and related issues, ordered by likelihood:
event_sequencewatermark leaving a session permanently unable to write; asks for the same fix (take the next seq from both tables); a v2.0.22 shared-DB occurrence is in the commentsopencode.dballocate seqs independently and collide (onsession_message.seq, with anevent-table variant in the comments)event.seqcollision on v2, triggered by a config reload while a session is runningIt would help to add your timeline and the 1728 vs 1772 numbers to #51411. Until it's fixed, a workaround is to keep v1 and v2 on separate data directories, or not run them at the same time. The bot will close this issue as a duplicate in 1 day unless you reply explaining how your problem is different.
opencode-agent commentedon Oct 12, 2026
We haven't heard back, so I'm closing this as a duplicate of #51411. That issue covers the same problem:
event_sequence.seqfalls behindMAX(event.seq)and the session can no longer write events. Please follow it there, and consider adding your timeline and your before/after values (event_sequence.seq1728 vsMAX(event.seq)1772) as a comment, since that would help. The broader problem of several processes sharing one database is tracked in #53146 and #49367. If your case turns out to be different, reply here and we can reopen. Thanks again for the detailed report and the workaround.