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
OOM and ~30 GB database: huge workspace-file diffs embedded in message summaries are re-serialized in full on every message.updated event #43551
When a text file in the OpenCode workspace keeps growing while a session is running (in my case an MCP server continuously appending its full API request/response log into the workspace — two log files reached 292 MB and 407 MB), OpenCode tracks the change and embeds the file's diff into message.summary.diffs on user messages.
This is compounded by the event store: every message.updated event re-serializes the entire message payload — including the huge embedded diff — as a new row in the event table. The same message is updated hundreds of times during a session, so the same multi-hundred-MB payload is written over and over.
Results:
opencode.db grew to 32.9 GB (event table alone: 29.4 GB)
OpenCode eventually crashed with OOM
Resuming the session crashes again with OOM, so the problematic session is permanently unopenable
Analysis
Measured on the 32.9 GB database:
table
size
event
29,407 MB
message
1,121 MB
part
510 MB
WAL
771 MB
event holds 513k rows; 97.6% were written within ~8 hours.
Largest single event row: 362 MB (message.updated.1). Payload begins: {"sessionID":"ses_A","info":{...,"role":"user","summary":{"diffs":[{"file":"/logs/api_20260820.jsonl","patch":"Index: ...
— the full unified diff of the 407 MB log file is embedded in the message.
Session ses_A: 250 message.updated events for the same message, averaging ~33 MB each → 8.4 GB of byte-identical payloads. Top 3 sessions account for ~24 GB.
Root-cause chain
A tool (here: an MCP server) continuously appends to a log file inside the workspace.
OpenCode diffs the file against its snapshot and embeds the uncapped diff into message.summary.diffs.
Every message.updated event stores a full copy of the message JSON in the event table (no truncation / dedup).
The message is updated hundreds of times → hundreds of full-size copies → tens of GB.
On session resume, giant messages are deserialized → OOM.
Steps to reproduce
Start OpenCode TUI, assign a long task to OpenCode to let it keep running.
Create a text file named test.txt in your OpenCode workspace.
Start a program that appends 1 MB of text into that file every 1 or 2 seconds.
Wait until OOM crash; restarting and resuming the session crashes again.
Diagnostics SQL:
SQL queries used
SELECT name, sum(pgsize)/1048576.0AS mb FROM dbstat GROUP BY name ORDER BY mb DESC;
SELECT length(data)/1048576AS mb, type, aggregate_id FROM event ORDER BY length(data) DESCLIMIT10;
SELECT aggregate_id, count(*), sum(length(data))/1048576AS mb FROM event GROUP BY aggregate_id ORDER BY mb DESCLIMIT10;
The root cause appears to be the same: the event table accumulates full message snapshots without any retention or compaction, which your large file being tracked would rapidly accelerate.
changed the title [-]OOM happened when a OpenCode tracked file's size grew to very large[/-][+]OOM and ~30 GB database: huge workspace-file diffs embedded in message summaries are re-serialized in full on every `message.updated` event[/+]on Aug 20, 2026
Confirming this exact failure mode on opencode-desktop 1.18.31 (Linux, Manjaro), engine at dev ebb7b76ec.
Measurements on my opencode.db (1.1 GB): event table 797 MB, of which 100 rows >1 MB = 555 MB. Largest single rows ~77 MB (message.updated.1). All from one session whose message.summary.diffs embedded full unified diffs of solver output files (foam_barrel_2d/<time>/U field files, project without git history so git-ignore filtering didn't apply).
Code path confirmed in source: packages/opencode/src/session/summary.ts:98 (computeDiff → snapshot.diffFull) → packages/opencode/src/snapshot/index.ts:736-737 (structuredPatch(..., { context: Number.MAX_SAFE_INTEGER }), no per-file or total byte limit) → packages/opencode/src/session/session.ts:629 (updateMessage publishes full info incl. the giant summary.diffs on every message.updated, each persisted as a new event row). Related: #33356 (no retention), #46138 (diffFull full patch text), #50089 (heap on resume).
Workaround that recovered my instance: DELETE FROM event WHERE LENGTH(data) > 1000000 (100 rows) + PRAGMA wal_checkpoint(TRUNCATE) + VACUUM → 1146 MB → 586 MB, integrity_check: ok, all sessions intact. Note for #43456: plain VACUUM in WAL mode spiked the WAL to ~590 MB mid-operation before the checkpoint reclaimed it — supporting the VACUUM INTO + swap approach.
Suggested fix direction: cap diff output (per-file + total bytes, keep numstat counts truthful, append [truncated] marker) at the diffFull emit loop; all patch consumers I could find are display-only, and patch is already optional in the schema, so truncation should be safe.
Another data point on 1.18.31 (unchanged in 1.18.32): opencode.db hit 82 GB, up from 6.9 GB about ten days earlier.
event is 76 GB of it. message.updated.1 rows account for 75.7 GB across 65,688 rows (mean 1.15 MB, max 31 MB). All other event types together come to about 330 MB.
Nearly all of that is info.summary.diffs[].patch for one file: a 31 MB, git-tracked .beads/issues.jsonl. Because diffFull() uses context: Number.MAX_SAFE_INTEGER, a +22/−21 line change produces a ~30 MB patch. summarize() then writes that patch into a new event row on every message update. One parent session and its subagents wrote 56 GB in a single day.
The 2 MB guard in snapshot/index.ts applies only to untracked files, so a large tracked file is never capped.
Aggregate queries over event (for example sum(length(data)) grouped by type) get OOM-killed on a 27 GB machine.
Workaround that keeps undo/revert: add the file to the repo's .git/info/exclude. snapshot.ignore() runs git check-ignore --no-index, which matches tracked files too, so they drop out of diffFull(). Combined with stripping the old payloads (UPDATE event SET data = json_remove(data, '$.info.summary.diffs') WHERE type = 'message.updated.1', then VACUUM, with opencode stopped), this reclaims the space.
What would fix it for everyone: bounded context in diffFull, a size cap that also covers tracked files, and not re-serializing unchanged diffs into each durable event (#42748, #50106).
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.
Activity
github-actions commentedon Aug 20, 2026
This issue might be a duplicate of existing issues. Please check:
eventtable: opencode.db reaches 13GB+, mostlymessage.updated.1snapshots (no retention/compaction) #33356: Unbounded growth of theeventtable —opencode.dbreaches 13GB+, mostlymessage.updated.1snapshots (no retention/compaction)The root cause appears to be the same: the
eventtable accumulates full message snapshots without any retention or compaction, which your large file being tracked would rapidly accelerate.[-]OOM happened when a OpenCode tracked file's size grew to very large[/-][+]OOM and ~30 GB database: huge workspace-file diffs embedded in message summaries are re-serialized in full on every `message.updated` event[/+]DScoNOIZ commentedon Sep 20, 2026
Confirming this exact failure mode on opencode-desktop 1.18.31 (Linux, Manjaro), engine at
dev ebb7b76ec.Measurements on my
opencode.db(1.1 GB):eventtable 797 MB, of which 100 rows>1 MB= 555 MB. Largest single rows ~77 MB (message.updated.1). All from one session whosemessage.summary.diffsembedded full unified diffs of solver output files (foam_barrel_2d/<time>/Ufield files, project without git history so git-ignore filtering didn't apply).Code path confirmed in source:
packages/opencode/src/session/summary.ts:98(computeDiff→snapshot.diffFull) →packages/opencode/src/snapshot/index.ts:736-737(structuredPatch(..., { context: Number.MAX_SAFE_INTEGER }), no per-file or total byte limit) →packages/opencode/src/session/session.ts:629(updateMessagepublishes fullinfoincl. the giantsummary.diffson everymessage.updated, each persisted as a neweventrow). Related: #33356 (no retention), #46138 (diffFull full patch text), #50089 (heap on resume).Workaround that recovered my instance:
DELETE FROM event WHERE LENGTH(data) > 1000000(100 rows) +PRAGMA wal_checkpoint(TRUNCATE)+VACUUM→ 1146 MB → 586 MB,integrity_check: ok, all sessions intact. Note for #43456: plainVACUUMin WAL mode spiked the WAL to ~590 MB mid-operation before the checkpoint reclaimed it — supporting theVACUUM INTO+ swap approach.Suggested fix direction: cap diff output (per-file + total bytes, keep numstat counts truthful, append
[truncated]marker) at thediffFullemit loop; all patch consumers I could find are display-only, andpatchis already optional in the schema, so truncation should be safe.craigvandotcom commentedon Sep 22, 2026
Another data point on 1.18.31 (unchanged in 1.18.32):
opencode.dbhit 82 GB, up from 6.9 GB about ten days earlier.eventis 76 GB of it.message.updated.1rows account for 75.7 GB across 65,688 rows (mean 1.15 MB, max 31 MB). All other event types together come to about 330 MB.info.summary.diffs[].patchfor one file: a 31 MB, git-tracked.beads/issues.jsonl. BecausediffFull()usescontext: Number.MAX_SAFE_INTEGER, a +22/−21 line change produces a ~30 MB patch.summarize()then writes that patch into a new event row on every message update. One parent session and its subagents wrote 56 GB in a single day.snapshot/index.tsapplies only to untracked files, so a large tracked file is never capped.event(for examplesum(length(data))grouped by type) get OOM-killed on a 27 GB machine.Workaround that keeps undo/revert: add the file to the repo's
.git/info/exclude.snapshot.ignore()runsgit check-ignore --no-index, which matches tracked files too, so they drop out ofdiffFull(). Combined with stripping the old payloads (UPDATE event SET data = json_remove(data, '$.info.summary.diffs') WHERE type = 'message.updated.1', thenVACUUM, with opencode stopped), this reclaims the space.What would fix it for everyone: bounded context in
diffFull, a size cap that also covers tracked files, and not re-serializing unchangeddiffsinto each durable event (#42748, #50106).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 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊