Repository navigation
Description
Status: recovered on the reporter side (2026-09-30).
Summary
The V1→V2 data migration (v2.0.19, first launch) silently dropped 5,547 message/part rows across 77 sessions (5,544 messages + 3 parts), logging only a WARN per row (Skipped V1 migration row). Every affected session dates from the v1.0.x era (Nov–Dec 2025) and most lost 91–99% of their messages to the V2 projection. The raw data is still intact in the legacy V1 tables, but the migration state is marked completed, so the content is now unreachable from the V2 UI.
Root cause (verified)
SessionV1.Assistant (packages/schema/src/v1/session.ts:465) declares agent: Schema.String as required. Assistant messages persisted by v1.0.x contain no agent key (the field was added in later V1 releases), so transformSession in packages/core/src/database/v1-migration.bun.ts (line ~232, Schema.decodeUnknownOption(SessionV1.Info)) fails validation and the row is skipped with reason=invalid-message.
Verification on my database:
- Sampled 200 skipped messages and checked them field-by-field against the
Assistantstruct: 200/200 are missing exactly one field —agent. All other fields (role,time,parentID,modelID,providerID,mode,path,cost,tokens,finish) validate cleanly. - All 77 affected sessions carry V1 version stamps
1.0.107–1.0.152; zero sessions from >=1.1 are affected.
Example skipped row (v1.0.126 assistant message, everything else valid):
{"role":"assistant","time":{"created":1764728238660,"completed":1764728251759},"parentID":"msg_...","modelID":"grok-code","providerID":"opencode","mode":"build","path":{"cwd":"...","root":"..."},"cost":0,"tokens":{"input":39843,"output":30,"reasoning":761,"cache":{"read":832,"write":0}},"finish":"tool-calls"}Impact
Total skipped: 5,547 rows (5,544 invalid-message + 3 invalid-part) = 2.8% of all 198,228 V1 messages, concentrated in 77 old sessions. Top 15:
| session | v1 version | created | v1 msgs | skipped | v2 proj | loss |
|---|---|---|---|---|---|---|
ses_51e000dd4ffecZExS70p7re8TW |
1.0.126 | 2025-12 | 465 | 447 | 17 | 96.1% |
ses_53cef287fffea6L8QL0Vh36lc0 |
1.0.114 | 2025-11 | 437 | 425 | 12 | 97.3% |
ses_545a1fb14ffe2p4XUYntmcE1DW |
1.0.110 | 2025-11 | 321 | 313 | 10 | 97.5% |
ses_536397eb6ffe7ppv4fRbJ52LKy |
1.0.119 | 2025-11 | 219 | 213 | 6 | 97.3% |
ses_528071fb1ffeu5FLtJhvAhnsYy |
1.0.123 | 2025-12 | 186 | 180 | 6 | 96.8% |
ses_54603e939ffe9md4O51n41r5Ez |
1.0.110 | 2025-11 | 178 | 173 | 7 | 97.2% |
ses_5421ef5e4ffe3eNE14CQ9Ex1Zm |
1.0.114 | 2025-11 | 795 | 172 | 621 | 21.6% |
ses_540b71d94ffegyqnqonRHseB9Y |
1.0.114 | 2025-11 | 165 | 159 | 9 | 96.4% |
ses_4da50572affeU1nGJbIQ7sZekp |
1.0.152 | 2025-12 | 173 | 159 | 16 | 91.9% |
ses_5351f23cbffe62TLi08jLFeOzH |
1.0.119 | 2025-11 | 151 | 147 | 3 | 97.4% |
ses_5021187f5ffeZSi7MgERXPJYAi |
1.0.134 | 2025-12 | 150 | 142 | 9 | 94.7% |
ses_51c106d0fffeI0Xzrp4Vejy1kS |
1.0.126 | 2025-12 | 142 | 139 | 4 | 97.9% |
ses_51b8dcae3ffeCgRgl8vn4ZZyhy |
1.0.129 | 2025-12 | 126 | 125 | 2 | 99.2% |
ses_536789327ffeJYZhvmrEuWBYws |
1.0.119 | 2025-11 | 131 | 124 | 7 | 94.7% |
ses_51e276fe3ffes7GBQnryZjA1hO |
1.0.126 | 2025-12 | 125 | 123 | 3 | 98.4% |
Full list of all 77 affected sessions
| session | v1 version | created | v1 msgs | skipped | v2 proj | loss |
|---|---|---|---|---|---|---|
ses_51e000dd4ffecZExS70p7re8TW |
1.0.126 | 2025-12 | 465 | 447 | 17 | 96.1% |
ses_53cef287fffea6L8QL0Vh36lc0 |
1.0.114 | 2025-11 | 437 | 425 | 12 | 97.3% |
ses_545a1fb14ffe2p4XUYntmcE1DW |
1.0.110 | 2025-11 | 321 | 313 | 10 | 97.5% |
ses_536397eb6ffe7ppv4fRbJ52LKy |
1.0.119 | 2025-11 | 219 | 213 | 6 | 97.3% |
ses_528071fb1ffeu5FLtJhvAhnsYy |
1.0.123 | 2025-12 | 186 | 180 | 6 | 96.8% |
ses_54603e939ffe9md4O51n41r5Ez |
1.0.110 | 2025-11 | 178 | 173 | 7 | 97.2% |
ses_5421ef5e4ffe3eNE14CQ9Ex1Zm |
1.0.114 | 2025-11 | 795 | 172 | 621 | 21.6% |
ses_540b71d94ffegyqnqonRHseB9Y |
1.0.114 | 2025-11 | 165 | 159 | 9 | 96.4% |
ses_4da50572affeU1nGJbIQ7sZekp |
1.0.152 | 2025-12 | 173 | 159 | 16 | 91.9% |
ses_5351f23cbffe62TLi08jLFeOzH |
1.0.119 | 2025-11 | 151 | 147 | 3 | 97.4% |
ses_5021187f5ffeZSi7MgERXPJYAi |
1.0.134 | 2025-12 | 150 | 142 | 9 | 94.7% |
ses_51c106d0fffeI0Xzrp4Vejy1kS |
1.0.126 | 2025-12 | 142 | 139 | 4 | 97.9% |
ses_51b8dcae3ffeCgRgl8vn4ZZyhy |
1.0.129 | 2025-12 | 126 | 125 | 2 | 99.2% |
ses_536789327ffeJYZhvmrEuWBYws |
1.0.119 | 2025-11 | 131 | 124 | 7 | 94.7% |
ses_51e276fe3ffes7GBQnryZjA1hO |
1.0.126 | 2025-12 | 125 | 123 | 3 | 98.4% |
ses_53771461affe4CnPSNJbunUzdL |
1.0.119 | 2025-11 | 127 | 118 | 7 | 92.9% |
ses_4fe4f09c9ffeuvfd1SGhMBrlqG |
1.0.134 | 2025-12 | 141 | 113 | 33 | 80.1% |
ses_503f00988ffeoK3algD9u4lgVi |
1.0.134 | 2025-12 | 103 | 100 | 3 | 97.1% |
ses_52722b547ffedVFTLSa3zciWQD |
1.0.123 | 2025-12 | 102 | 98 | 4 | 96.1% |
ses_54a8a7505ffeFXnYtFntTE5MkM |
1.0.107 | 2025-11 | 262 | 95 | 13 | 36.3% |
ses_53545dcf3ffewWY8BRiZGmjcp9 |
1.0.119 | 2025-11 | 100 | 95 | 5 | 95.0% |
ses_502333976ffe9pqlkjYcy3ksgi |
1.0.134 | 2025-12 | 99 | 94 | 5 | 94.9% |
ses_536b90f61ffeWJLtkV3XMGwWcQ |
1.0.119 | 2025-11 | 88 | 87 | 1 | 98.9% |
ses_52729b716ffeQSBGAZOd7MlZdu |
1.0.123 | 2025-12 | 89 | 86 | 3 | 96.6% |
ses_537989628ffe11jEpqeUNJEI0R |
1.0.119 | 2025-11 | 82 | 79 | 4 | 96.3% |
ses_4db01d874ffexK87fk2rt3bJ2V |
1.0.152 | 2025-12 | 89 | 78 | 15 | 87.6% |
ses_5422ef80dffeV1tsdRbnaLN99I |
1.0.110 | 2025-11 | 77 | 75 | 2 | 97.4% |
ses_5407e955affe9cJllWRRfPndLu |
1.0.114 | 2025-11 | 77 | 74 | 3 | 96.1% |
ses_54089c199ffef8Gc0xih7kdj2W |
1.0.114 | 2025-11 | 71 | 69 | 2 | 97.2% |
ses_53692e872ffe35A6pzOMeCSflC |
1.0.119 | 2025-11 | 70 | 68 | 2 | 97.1% |
ses_540505ba1ffeli7Xa1h5J7wtvD |
1.0.114 | 2025-11 | 69 | 66 | 3 | 95.7% |
ses_536c818e0ffezFb2wgJElolpsG |
1.0.119 | 2025-11 | 67 | 64 | 3 | 95.5% |
ses_5403d1ce8ffeB9j9WDjjEH7pE7 |
1.0.114 | 2025-11 | 66 | 62 | 4 | 93.9% |
ses_535a63703ffeKhjDUy0Lkr6XbO |
1.0.119 | 2025-11 | 60 | 59 | 1 | 98.3% |
ses_51cf532e6ffekHNcXBPa6lIfjG |
1.0.128 | 2025-12 | 63 | 58 | 6 | 92.1% |
ses_50381f9e2ffenWzH7pXjC7pt7H |
1.0.134 | 2025-12 | 59 | 57 | 2 | 96.6% |
ses_503ff21f4ffeamKQcBUwruuHDB |
1.0.134 | 2025-12 | 58 | 55 | 3 | 94.8% |
ses_536aad44bffe6L51dTRRIt5V5Q |
1.0.119 | 2025-11 | 55 | 54 | 1 | 98.2% |
ses_512df9ba2ffegCtDDsLlLexhHp |
1.0.133 | 2025-12 | 59 | 52 | 8 | 88.1% |
ses_4f80f8ad2fferlRmsYkOojNxkd |
1.0.134 | 2025-12 | 60 | 52 | 7 | 86.7% |
ses_4fe9e0b3affeHEWDCfwL66te8Y |
1.0.134 | 2025-12 | 46 | 41 | 6 | 89.1% |
ses_527529699ffekuNWoXDWa04JJS |
1.0.123 | 2025-12 | 42 | 40 | 2 | 95.2% |
ses_527155f95ffeuGkb5mK8cwyu6z |
1.0.123 | 2025-12 | 40 | 38 | 3 | 95.0% |
ses_512f23b10ffeQ7ZSR63YUt0ilo |
1.0.133 | 2025-12 | 38 | 34 | 4 | 89.5% |
ses_511f2166fffez21d76ueDDZPNK |
1.0.133 | 2025-12 | 34 | 33 | 1 | 97.1% |
ses_5367e3a7dffeVYzvyH3VG91TmC |
1.0.119 | 2025-11 | 33 | 32 | 1 | 97.0% |
ses_51e2cd440ffe4vCBpu1X3iRvZp |
1.0.126 | 2025-12 | 33 | 32 | 1 | 97.0% |
ses_51e0ce28affeuS6JBDHeYAO269 |
1.0.126 | 2025-12 | 31 | 30 | 1 | 96.8% |
ses_4fef99f49ffe7AzGszWQLaA17z |
1.0.134 | 2025-12 | 33 | 29 | 4 | 87.9% |
ses_5409fc3f9ffeBObfuE4ePlsIu4 |
1.0.114 | 2025-11 | 28 | 27 | 2 | 96.4% |
ses_52702c520ffejNachXvsjNQwTb |
1.0.123 | 2025-12 | 28 | 27 | 1 | 96.4% |
ses_5369daf51ffeOyhSD8Hg3mavh5 |
1.0.119 | 2025-11 | 27 | 26 | 1 | 96.3% |
ses_54225fe3effeBTeLR9EikpM3WO |
1.0.114 | 2025-11 | 25 | 24 | 1 | 96.0% |
ses_53b8a25dcffeyiYprcalkBapZR |
1.0.114 | 2025-11 | 24 | 23 | 1 | 95.8% |
ses_5272e99c0ffeBcS3WvUv48zO9f |
1.0.123 | 2025-12 | 24 | 23 | 1 | 95.8% |
ses_4c9678c39ffeZ4WL3BQUPK7L7i |
1.0.152 | 2025-12 | 26 | 22 | 4 | 84.6% |
ses_4feced283ffey4090hEGlEVHLb |
1.0.134 | 2025-12 | 33 | 21 | 11 | 63.6% |
ses_52732aa4fffet0I4gKZNaruIam |
1.0.123 | 2025-12 | 21 | 20 | 1 | 95.2% |
ses_51c97696affewYnQwlntmI9x3b |
1.0.128 | 2025-12 | 20 | 18 | 2 | 90.0% |
ses_502742f7cffep4GWrZk50g3D8h |
1.0.134 | 2025-12 | 14 | 13 | 1 | 92.9% |
ses_4d99cad35ffe2ukM9YFBRctS1N |
1.0.152 | 2025-12 | 18 | 10 | 9 | 55.6% |
ses_5273b0850ffexoa5d9eqyqGsT0 |
1.0.123 | 2025-12 | 7 | 6 | 1 | 85.7% |
ses_53cfbe0efffe15tvaozpS6Hd47 |
1.0.114 | 2025-11 | 5 | 4 | 1 | 80.0% |
ses_4d9ac2cc0ffebN5EmySy2Z9y7n |
1.0.152 | 2025-12 | 6 | 4 | 4 | 66.7% |
ses_546051382ffet6DlhWKwa1j8Kj |
1.0.110 | 2025-11 | 4 | 3 | 1 | 75.0% |
ses_540a1ac3fffeBFaumwXu6geKfn |
1.0.114 | 2025-11 | 5 | 3 | 4 | 60.0% |
ses_4f8c9cc74ffe9h7ULToonrE2J7 |
1.0.134 | 2025-12 | 4 | 3 | 2 | 75.0% |
ses_4fecfb533ffezITjGHIXG42rdU |
1.0.134 | 2025-12 | 3 | 2 | 1 | 66.7% |
ses_4fe425edaffemCF9sPEN9HGwju |
1.0.134 | 2025-12 | 3 | 2 | 1 | 66.7% |
ses_536d64aceffeJjBs33FJzP22JF |
1.0.119 | 2025-11 | 2 | 1 | 1 | 50.0% |
ses_536b9639bffeYpiwvFKLF2BviZ |
1.0.119 | 2025-11 | 2 | 1 | 1 | 50.0% |
ses_536ab017affeAyV6AkmI2P4LSi |
1.0.119 | 2025-11 | 2 | 1 | 1 | 50.0% |
ses_5367e960effeK10KUWDikCM0yy |
1.0.119 | 2025-11 | 2 | 1 | 1 | 50.0% |
ses_5363a0a65ffem7ObXEnhcLPwGS |
1.0.119 | 2025-11 | 2 | 1 | 1 | 50.0% |
ses_5273ccdfdffeh2OLkZzK7TYzhE |
1.0.123 | 2025-12 | 2 | 1 | 1 | 50.0% |
ses_527350ce9ffeKAoLlJZ3oImHag |
1.0.123 | 2025-12 | 2 | 1 | 1 | 50.0% |
ses_51b827cc3ffemmIc6euH8bGIVe |
1.0.129 | 2025-12 | 2 | 1 | 2 | 50.0% |
The ~18 "passed" messages in a 96%-loss session are the user rows (user messages don't require agent), which is why the sessions still appear in the list but are essentially empty.
Data is not destroyed
- Legacy V1 tables are untouched:
message= 198,228 rows (1.43 GB),part= 818,341 rows (1.79 GB) — including every skipped row. kv['migration.v1-v2'] = {"phase":"completed"}— the migration will not re-run, and there is no documented repair path to re-project an individual session.
Environment
- OpenCode: v2.0.19 (official binary)
- OS: Fedora 44, kernel 7.2.7, x86_64
- V1 history: sessions from v1.0.107 (2025-11) through v1.18.32 (2026-09)
- DB:
~/.local/share/opencode/opencode.db(25.8 GB)
Suggestion
- Make
agentoptional (or default it) when decoding legacy V1 assistant messages — or backfill it from the session row during migration. The same latent problem exists for any other field V1 added after 1.0.x that is required today; a per-field tolerance with a warning would be safer than a hard skip. - Provide a repair/re-run path: allow re-migrating a single session (e.g. clear its projection + bump the cursor) so data like this is recoverable without hand-editing SQLite.
Happy to share the full skip-log extract or additional samples.
Activity
github-actions commentedon Sep 30, 2026
This issue doesn't fully meet our contributing guidelines.
What needs to be fixed:
Please edit this issue to address the above within 2 hours, or it will be automatically closed.
This issue might be a duplicate of existing issues. Please check:
agent, cause session-loading failures.If you believe this was flagged incorrectly, please let a maintainer know.
hallelujah-shih commentedon Sep 30, 2026
Follow-up with full verification results, one correction to my suggestion, and a recovery procedure I validated against my own database.
Full verification (all 5,544 rows, not the 200-sample)
assistantmessages missing exactly one field:agent. No other required field is missing anywhere. (15 rows additionally carrysummary: true, which is valid per theAssistantschema — boolean, not the user-summary object.)modefield:build×5,393,plan×149,general×2.invalid-partrows are all compaction markers with body{"type":"compaction"}(2 inses_545a1fb14ffe2p4XUYntmcE1DW, 1 inses_54603e939ffe9md4O51n41r5Ez).Correction to Suggestion #1
The V1
session.agentcolumn is NULL for 76 of the 77 affected sessions (only one hasbuild), so it cannot serve as the backfill source. The correct source is the message's ownmodefield — in v1.0.x,modeis where the agent name was stored. Backfillingagent = modemakes all 5,544 rows passSessionV1.Assistantvalidation with zero other changes (verified row-by-row above).Recovery procedure (validated on my DB, dry-run only so far)
Notes:
sessiontable, so V2-native sessions are untouched by a re-run.transformSessiondeletes each session's projection before re-inserting, so the re-run is idempotent for the other 2,902 sessions.{"type":"compaction"}body doesn't satisfy the currentCompactionPartschema); impact is cosmetic.hallelujah-shih commentedon Sep 30, 2026
Correction: true loss is 6,238 rows / 92 sessions — and the full root-cause chain is now sourced
After deeper forensics (including reading the V1 source at the relevant tags), the numbers in the original report undercount the loss, and the root cause has a longer history than "v2 schema too strict". Corrected findings:
1. True loss: 6,238 assistant rows across 92 sessions (not 5,544 / 77)
The skip log itself is incomplete. Deriving the loss set from the DB instead of the log:
The log contains 5,544 of these 6,238 skip lines. 694 lines are missing from the log (15 sessions have zero skip lines despite 2–121 missing-
agentrows each). All 5,547 present lines fall in one 2-second burst (2026-09-28T06:49:07.286Z→06:49:09.147Z); the migration ran exactly once (single burst, no resume). So the WARN stream apparently dropped lines under load — the log is a lower bound, not an audit trail. (Secondary bug worth noting: the migration should emit a final summary with total skipped counts rather than relying on per-row WARNs.)All 6,238 rows are missing exactly one field (
agent; 3 of them also lack optionalfinish). All 6,238 carrymode— the pre-rename name of the same field (distribution:build5,845 /plan386 /ddd-architect5 /general2).The mapping is provably a rename, not a guess: of all 177,699 assistant rows that do carry
agent, 100% also carrymodewithagent == mode(zero mismatches). Backfillingagent = modereconstructs exactly what the writer would have produced.2. Full root-cause chain (verified against V1 source tags)
modeonly.packages/opencode/src/session/message-v2.tsat tagv1.0.152:Assistanthasmode: z.string()and noagent.agent: z.string()(required) toAssistantand markedmode@deprecated— a rename, with no backfill of legacy rows. This was harmless inside V1 because the read path performs no runtime validation:agentload fine; consumers seeagent === undefinedand fall back.packages/opencode/src/storage/json-migration.tsimported all legacy JSON into the new tables as a faithful copy (11,413 messages / 45,961 parts / 336 todos in my DB; the uniformtime_updatedon those rows is the code'sdata.time?.updated ?? nowfallback). Legacy rows still lackedagent— no repair was attempted, none was needed for V1.agent. 91 of the 92 affected sessions were never touched again after 2025-12-25, so their legacy rows stayed in the old shape.Schema.decodeUnknownOption(SessionV1.Info), whereSessionV1.Assistantdeclaresagentrequired (packages/schema/src/v1/session.ts:465). Every legacy row fails → skipped with a per-row WARN.So: the field gap was born in V1 (required field introduced without backfill, tolerated by V1's unvalidated reads) and only became data loss when V2's migration made validation strict.
3. Corrected recovery
Backfill must be condition-based, not log-derived:
then
DELETE FROM kv WHERE key='migration.v1-v2';and restart — the migration re-runs and re-projects everything. (V1session.agentis NULL for 76/77 of the originally listed sessions, so the session row is not a usable backfill source; the message's ownmodeis — same field, pre-rename.)4. Residuals after recovery (by design, not loss)
Even after backfill + re-run, some sessions' V2 projection will legitimately contain fewer rows than the V1 table, because
transformSessioncollapses or drops certain shapes: compaction pairs (user + summary assistant → one compaction row), subagent task messages (parent hassubtaskpart +tasktool call), subtask-only user messages, and compaction user messages without a paired summary. None of these produce skip warnings; they are V2's intended projection semantics.The 3
{"type":"compaction"}parts from the V1.0.x era remain undecodable (V1.3+CompactionPartrequiresauto: z.boolean()); their messages' compaction pairing is unaffected.All 92 affected sessions (corrected table)
ses_51e000dd4ffecZExS70p7re8TWses_53cef287fffea6L8QL0Vh36lc0ses_545a1fb14ffe2p4XUYntmcE1DWses_54a8a7505ffeFXnYtFntTE5MkMses_536397eb6ffe7ppv4fRbJ52LKyses_528071fb1ffeu5FLtJhvAhnsYyses_5421ef5e4ffe3eNE14CQ9Ex1Zmses_54603e939ffe9md4O51n41r5Ezses_4da50572affeU1nGJbIQ7sZekpses_540b71d94ffegyqnqonRHseB9Yses_5351f23cbffe62TLi08jLFeOzHses_5021187f5ffeZSi7MgERXPJYAises_51c106d0fffeI0Xzrp4Vejy1kSses_51b8dcae3ffeCgRgl8vn4ZZyhyses_536789327ffeJYZhvmrEuWBYwsses_51e276fe3ffes7GBQnryZjA1hOses_56b24c609ffeQa1sdByEA85qbgses_53771461affe4CnPSNJbunUzdLses_4fe4f09c9ffeuvfd1SGhMBrlqGses_5a30dd6c7ffemSZZ16Z5TRt5aOses_503f00988ffeoK3algD9u4lgVises_52722b547ffedVFTLSa3zciWQDses_53545dcf3ffewWY8BRiZGmjcp9ses_502333976ffe9pqlkjYcy3ksgises_536b90f61ffeWJLtkV3XMGwWcQses_52729b716ffeQSBGAZOd7MlZduses_537989628ffe11jEpqeUNJEI0Rses_4db01d874ffexK87fk2rt3bJ2Vses_5422ef80dffeV1tsdRbnaLN99Ises_5407e955affe9cJllWRRfPndLuses_54089c199ffef8Gc0xih7kdj2Wses_53692e872ffe35A6pzOMeCSflCses_540505ba1ffeli7Xa1h5J7wtvDses_536c818e0ffezFb2wgJElolpsGses_5403d1ce8ffeB9j9WDjjEH7pE7ses_5a3a81d07ffeZz1n9OxKtx7HHfses_535a63703ffeKhjDUy0Lkr6XbOses_51cf532e6ffekHNcXBPa6lIfjGses_50381f9e2ffenWzH7pXjC7pt7Hses_503ff21f4ffeamKQcBUwruuHDBses_536aad44bffe6L51dTRRIt5V5Qses_56af1ef32ffeC9WIOD1wIze29Kses_512df9ba2ffegCtDDsLlLexhHpses_4f80f8ad2fferlRmsYkOojNxkdses_5a3c157d6ffeCNe3uG2Vy0SM7rses_4fe9e0b3affeHEWDCfwL66te8Yses_527529699ffekuNWoXDWa04JJSses_527155f95ffeuGkb5mK8cwyu6zses_54b2554a9ffeRnqyOpwEb6NcS8ses_512f23b10ffeQ7ZSR63YUt0iloses_511f2166fffez21d76ueDDZPNKses_51e2cd440ffe4vCBpu1X3iRvZpses_5367e3a7dffeVYzvyH3VG91TmCses_51e0ce28affeuS6JBDHeYAO269ses_4fef99f49ffe7AzGszWQLaA17zses_5a3d9f448ffeOi61t5PJh96k28ses_52702c520ffejNachXvsjNQwTbses_5409fc3f9ffeBObfuE4ePlsIu4ses_5a3f5c8bdffe1wB8c6RjzPSEBxses_5369daf51ffeOyhSD8Hg3mavh5ses_54225fe3effeBTeLR9EikpM3WOses_5272e99c0ffeBcS3WvUv48zO9fses_53b8a25dcffeyiYprcalkBapZRses_4c9678c39ffeZ4WL3BQUPK7L7ises_4feced283ffey4090hEGlEVHLbses_52732aa4fffet0I4gKZNaruIamses_51c97696affewYnQwlntmI9x3bses_5a3ff5e69ffeZq16IsXw5qblKPses_57f062d2dffeWfc61jqx1nqL4Xses_502742f7cffep4GWrZk50g3D8hses_4d99cad35ffe2ukM9YFBRctS1Nses_54a8b65ebffemKv54IgTdkR7xFses_5a273fb42ffeDR3MH7jkft4Gavses_5273b0850ffexoa5d9eqyqGsT0ses_5a38b209bffeunjXACA1P7rwweses_5a3f5661bffeAFFiBHY7k8oQSPses_4d9ac2cc0ffebN5EmySy2Z9y7nses_53cfbe0efffe15tvaozpS6Hd47ses_546051382ffet6DlhWKwa1j8Kjses_4f8c9cc74ffe9h7ULToonrE2J7ses_540a1ac3fffeBFaumwXu6geKfnses_5a3c85067ffeghHZq1eN43BOiXses_4fe425edaffemCF9sPEN9HGwjuses_4fecfb533ffezITjGHIXG42rdUses_51b827cc3ffemmIc6euH8bGIVeses_527350ce9ffeKAoLlJZ3oImHagses_5273ccdfdffeh2OLkZzK7TYzhEses_5363a0a65ffem7ObXEnhcLPwGSses_5367e960effeK10KUWDikCM0yyses_536ab017affeAyV6AkmI2P4LSises_536b9639bffeYpiwvFKLF2BviZses_536d64aceffeJjBs33FJzP22JFhallelujah-shih commentedon Sep 30, 2026
Correction: true loss is 6,238 rows / 92 sessions — and the full root-cause chain is now sourced
After deeper forensics (including reading the V1 source at the relevant tags), the numbers in the original report undercount the loss, and the root cause has a longer history than "v2 schema too strict". Corrected findings:
1. True loss: 6,238 assistant rows across 92 sessions (not 5,544 / 77)
The skip log itself is incomplete. Deriving the loss set from the DB instead of the log:
The log contains 5,544 of these 6,238 skip lines. 694 lines are missing from the log (15 sessions have zero skip lines despite 2–121 missing-
agentrows each). All 5,547 present lines fall in one 2-second burst (2026-09-28T06:49:07.286Z→06:49:09.147Z); the migration ran exactly once (single burst, no resume). So the WARN stream apparently dropped lines under load — the log is a lower bound, not an audit trail. (Secondary bug worth noting: the migration should emit a final summary with total skipped counts rather than relying on per-row WARNs.)All 6,238 rows are missing exactly one field (
agent; 3 of them also lack optionalfinish). All 6,238 carrymode— the pre-rename name of the same field (distribution:build5,845 /plan386 /ddd-architect5 /general2).The mapping is provably a rename, not a guess: of all 177,699 assistant rows that do carry
agent, 100% also carrymodewithagent == mode(zero mismatches). Backfillingagent = modereconstructs exactly what the writer would have produced.2. Full root-cause chain (verified against V1 source tags)
modeonly.packages/opencode/src/session/message-v2.tsat tagv1.0.152:Assistanthasmode: z.string()and noagent.agent: z.string()(required) toAssistantand markedmode@deprecated— a rename, with no backfill of legacy rows. This was harmless inside V1 because the read path performs no runtime validation:agentload fine; consumers seeagent === undefinedand fall back.packages/opencode/src/storage/json-migration.tsimported all legacy JSON into the new tables as a faithful copy (11,413 messages / 45,961 parts / 336 todos in my DB; the uniformtime_updatedon those rows is the code'sdata.time?.updated ?? nowfallback). Legacy rows still lackedagent— no repair was attempted, none was needed for V1.agent. 91 of the 92 affected sessions were never touched again after 2025-12-25, so their legacy rows stayed in the old shape.Schema.decodeUnknownOption(SessionV1.Info), whereSessionV1.Assistantdeclaresagentrequired (packages/schema/src/v1/session.ts:465). Every legacy row fails → skipped with a per-row WARN.So: the field gap was born in V1 (required field introduced without backfill, tolerated by V1's unvalidated reads) and only became data loss when V2's migration made validation strict.
3. Corrected recovery
Backfill must be condition-based, not log-derived:
then
DELETE FROM kv WHERE key='migration.v1-v2';and restart — the migration re-runs and re-projects everything. (V1session.agentis NULL for 76/77 of the originally listed sessions, so the session row is not a usable backfill source; the message's ownmodeis — same field, pre-rename.)4. Second bug: re-running the migration destroys V2-appended history
While validating the recovery on a full DB copy, I found that the per-session projection is an unconditional delete + re-insert of
session_message(v1-migration.bun.ts:tx.delete(SessionMessageTable).where(eq(SessionMessageTable.session_id, next.id))). The migration iterates every V1sessionrow, so if a V1-origin session was resumed in V2 after the original migration, all messages V2 appended to it (user/assistant turns, compaction, idle markers) are wiped on any re-run. In my DB: 2,795 V1-origin sessions carry 3,635 rows whose ids are not in the V1messagetable; 3,202 of those are projection artifacts (regenerated, harmless), but 433 rows in 3 sessions are genuine V2 conversation history that a re-run would silently destroy. Mitigation used in my recovery: temporarily move the affected sessions' V1 rows out of thesessiontable for the re-run duration, then restore them (V2 reads onlysession_message/session_v2, so this is lossless).5. Recovery validated on a full DB copy
Backfill + reset + re-run performed on a 25 GB
.backupcopy (isolatedXDG_DATA_HOME,serve --port 49375): all 6,238 rows backfilled (0 skipped), migration re-ran in ~2 min, 91 of the 92 affected sessions fully recovered (V2 projection grew 968 → 7,285 rows; the one remaining 1-row gap is compaction-pair collapse, by design), zero new skip warnings, all other 2,887 sessions' projections byte-identical before/after.6. Residuals after recovery (by design, not loss)
Even after backfill + re-run, some sessions' V2 projection will legitimately contain fewer rows than the V1 table, because
transformSessioncollapses or drops certain shapes: compaction pairs (user + summary assistant → one compaction row), subagent task messages (parent hassubtaskpart +tasktool call), subtask-only user messages, and compaction user messages without a paired summary. None of these produce skip warnings; they are V2's intended projection semantics.The 3
{"type":"compaction"}parts from the V1.0.x era remain undecodable (V1.3+CompactionPartrequiresauto: z.boolean()); their messages' compaction pairing is unaffected.All 92 affected sessions (corrected table)
ses_51e000dd4ffecZExS70p7re8TWses_53cef287fffea6L8QL0Vh36lc0ses_545a1fb14ffe2p4XUYntmcE1DWses_54a8a7505ffeFXnYtFntTE5MkMses_536397eb6ffe7ppv4fRbJ52LKyses_528071fb1ffeu5FLtJhvAhnsYyses_5421ef5e4ffe3eNE14CQ9Ex1Zmses_54603e939ffe9md4O51n41r5Ezses_4da50572affeU1nGJbIQ7sZekpses_540b71d94ffegyqnqonRHseB9Yses_5351f23cbffe62TLi08jLFeOzHses_5021187f5ffeZSi7MgERXPJYAises_51c106d0fffeI0Xzrp4Vejy1kSses_51b8dcae3ffeCgRgl8vn4ZZyhyses_536789327ffeJYZhvmrEuWBYwsses_51e276fe3ffes7GBQnryZjA1hOses_56b24c609ffeQa1sdByEA85qbgses_53771461affe4CnPSNJbunUzdLses_4fe4f09c9ffeuvfd1SGhMBrlqGses_5a30dd6c7ffemSZZ16Z5TRt5aOses_503f00988ffeoK3algD9u4lgVises_52722b547ffedVFTLSa3zciWQDses_53545dcf3ffewWY8BRiZGmjcp9ses_502333976ffe9pqlkjYcy3ksgises_536b90f61ffeWJLtkV3XMGwWcQses_52729b716ffeQSBGAZOd7MlZduses_537989628ffe11jEpqeUNJEI0Rses_4db01d874ffexK87fk2rt3bJ2Vses_5422ef80dffeV1tsdRbnaLN99Ises_5407e955affe9cJllWRRfPndLuses_54089c199ffef8Gc0xih7kdj2Wses_53692e872ffe35A6pzOMeCSflCses_540505ba1ffeli7Xa1h5J7wtvDses_536c818e0ffezFb2wgJElolpsGses_5403d1ce8ffeB9j9WDjjEH7pE7ses_5a3a81d07ffeZz1n9OxKtx7HHfses_535a63703ffeKhjDUy0Lkr6XbOses_51cf532e6ffekHNcXBPa6lIfjGses_50381f9e2ffenWzH7pXjC7pt7Hses_503ff21f4ffeamKQcBUwruuHDBses_536aad44bffe6L51dTRRIt5V5Qses_56af1ef32ffeC9WIOD1wIze29Kses_512df9ba2ffegCtDDsLlLexhHpses_4f80f8ad2fferlRmsYkOojNxkdses_5a3c157d6ffeCNe3uG2Vy0SM7rses_4fe9e0b3affeHEWDCfwL66te8Yses_527529699ffekuNWoXDWa04JJSses_527155f95ffeuGkb5mK8cwyu6zses_54b2554a9ffeRnqyOpwEb6NcS8ses_512f23b10ffeQ7ZSR63YUt0iloses_511f2166fffez21d76ueDDZPNKses_51e2cd440ffe4vCBpu1X3iRvZpses_5367e3a7dffeVYzvyH3VG91TmCses_51e0ce28affeuS6JBDHeYAO269ses_4fef99f49ffe7AzGszWQLaA17zses_5a3d9f448ffeOi61t5PJh96k28ses_52702c520ffejNachXvsjNQwTbses_5409fc3f9ffeBObfuE4ePlsIu4ses_5a3f5c8bdffe1wB8c6RjzPSEBxses_5369daf51ffeOyhSD8Hg3mavh5ses_54225fe3effeBTeLR9EikpM3WOses_5272e99c0ffeBcS3WvUv48zO9fses_53b8a25dcffeyiYprcalkBapZRses_4c9678c39ffeZ4WL3BQUPK7L7ises_4feced283ffey4090hEGlEVHLbses_52732aa4fffet0I4gKZNaruIamses_51c97696affewYnQwlntmI9x3bses_5a3ff5e69ffeZq16IsXw5qblKPses_57f062d2dffeWfc61jqx1nqL4Xses_502742f7cffep4GWrZk50g3D8hses_4d99cad35ffe2ukM9YFBRctS1Nses_54a8b65ebffemKv54IgTdkR7xFses_5a273fb42ffeDR3MH7jkft4Gavses_5273b0850ffexoa5d9eqyqGsT0ses_5a38b209bffeunjXACA1P7rwweses_5a3f5661bffeAFFiBHY7k8oQSPses_4d9ac2cc0ffebN5EmySy2Z9y7nses_53cfbe0efffe15tvaozpS6Hd47ses_546051382ffet6DlhWKwa1j8Kjses_4f8c9cc74ffe9h7ULToonrE2J7ses_540a1ac3fffeBFaumwXu6geKfnses_5a3c85067ffeghHZq1eN43BOiXses_4fe425edaffemCF9sPEN9HGwjuses_4fecfb533ffezITjGHIXG42rdUses_51b827cc3ffemmIc6euH8bGIVeses_527350ce9ffeKAoLlJZ3oImHagses_5273ccdfdffeh2OLkZzK7TYzhEses_5363a0a65ffem7ObXEnhcLPwGSses_5367e960effeK10KUWDikCM0yyses_536ab017affeAyV6AkmI2P4LSises_536b9639bffeYpiwvFKLF2BviZses_536d64aceffeJjBs33FJzP22JFgithub-actions commentedon Sep 30, 2026
This issue has been automatically closed because it was not updated to meet our contributing guidelines within the 2-hour window.
Feel free to open a new issue that follows our issue templates.
hallelujah-shih commentedon Sep 30, 2026
Production recovery complete — runbook, results, operational notes
The recovery has now been executed on the live database (v2.0.20, 25.8 GB). Full procedure, in order:
opencode service stop— wait until the process has actually exited (note 1 below!)cp opencode.db opencode.db.pre-repair(plus-walif non-empty)session_messagecontains rows with ids absent from the V1messagetable and post-migration timestamps (in my DB: 3 sessions, 433 rows of genuine V2 conversation history). Temporarily move their V1 rows aside so the re-run skips them:session_v2/session_message, so this is lossless; rows come back in step 9.)DELETE FROM kv WHERE key = 'migration.v1-v2';opencode service start— migration re-runs on boot (~2 min for 2,979 sessions)kv.migration.v1-v2until{"phase":"completed"}INSERT ... SELECTfrom the_bak_*tables, verify counts, drop the tables)VACUUM; PRAGMA wal_checkpoint(TRUNCATE);— the migration's mass DELETE had left a 19.6 GB freelist; the file shrank 25.8 GB → 6.2 GB.Results
mode)agent-missing rows remain; session/message/part counts identical to baselineopencode stats --allnow reflects the recovered content (Nov–Dec 2025 activity restored)Operational notes (bites encountered on the live run)
service stopsends SIGTERM, but the service waits for background fibers — I observed amodels.devfetch timing out at ~5 min plus MCP reconnection spans. A stop script that gives up after ~1 minute will misreport failure and must not touch the DB; poll for up to 12 min (SIGKILL is safe: SQLite WAL is crash-safe).[-]V1→V2 migration silently drops v1.0.x-era assistant messages: required `agent` field fails schema validation (77 sessions lost 91–99% of content)[/-][+]V1→V2 migration silently drops v1.0.x-era assistant messages: required `agent` field fails schema validation — 6,238 messages across 92 sessions[/+]