Skip to content

migration: V1→V2 session migration hard-fails with RangeError: Out of memory on sessions with large inline base64 attachments #62049

Closed as duplicate

Description

@0xAlok

Summary

The V1→V2 session data migration aborts every time it reaches a V1 session whose part rows contain large inline base64 attachments, leaving the whole pre-upgrade session list empty. The resident background service then repeats the failure on every boot and never advances its resume cursor.

Environment

  • opencode version: 2.2.5 (channel: latest)
  • OS: Linux 6.18.13-arch1-1 (Arch, x64)
  • Terminal: xterm-256color (foot)
  • Shell: bash
  • Install/channel: official bun build via mise (mise installs/opencode/2.2.5)
  • Active plugins (loaded at runtime): oh-my-openagent@latest, opencode-antigravity-auth@latest, opencode-chrome-annotation@latest

Reproduction

  1. Have a V1 database (from 1.x) with a session whose part contains a data:application/pdf;base64,... inline attachment of a few hundred MB. In my case one assistant message held three file parts: 530 MB, 283 MB and 83 MB of base64 in their url fields (~900 MB total).
  2. Upgrade to 2.2.5 and boot the shared service. Schema migrations complete; the background migration.v1-v2 data migration starts.
  3. Wait about 10 seconds.

Expected Behavior

The migration should either complete, or skip an oversized session with a recorded warning and keep going. The resume cursor (kv key migration.v1-v2) should advance each boot so progress converges.

Actual Behavior

The migration dies before any user-visible error, with this in the server log:

level=ERROR role=server message="V1 migration failed" cause="Cause([Die(RangeError: Out of memory)])"

Observed five times across five service restarts, always at the same state:

  • kv['migration.v1-v2'] stayed frozen at {"phase":"sessions","cursor":"<last ok session id>"} - it never flipped to completed.
  • SELECT COUNT(*) FROM session_v2 stayed at 21 while session held 501 rows.
  • The session picker showed only the sessions migrated before the first failure (plus newly created ones). Everything older was invisible across every project.
  • Available system memory was ~5.6 GB of 15 GB plus unused swap, so this is an allocation limit, not real RAM exhaustion. Loading one 530 MB base64 string plus its JSON structure into JS objects is enough to trip RangeError: Out of memory.

The failing session is always the next V1 session below the frozen cursor: the loop picks SELECT ... FROM session WHERE id < :cursor ORDER BY id DESC LIMIT 1, loads all of that session's message and part rows and their JSON payloads into memory in one go, and dies there. As long as the next session contains an inline mega-attachment, the migration can never pass it.

Workaround (verified)

Advance the resume cursor past the offending session so the loop starts below it:

sqlite3 "$HOME/.local/share/opencode/opencode.db" \
  "UPDATE kv SET value=json_set(value,'$.cursor','<offending-session-id>') WHERE key='migration.v1-v2';"
opencode service restart

After that the migration resumed and completed ({"phase":"completed"}, all remaining sessions imported). Secondary fix that reclaimed ~1.8 GB: strip the base64 url in the offending part rows with a small script and VACUUM (dB shrank from 2.3 GB to 559 MB).

Suggestion: a size guard - either stream per-part conversion instead of materializing all rows of a session, or skip sessions above a threshold with the same warnings channel already used for invalid rows (Skipped V1 migration row), and note the skip in the migration state so users can be told which session was left behind.

Additional Context

  • Frequency: 100% reproducible per boot until the cursor is advanced past the bad session.
  • The offending attachment had been pasted into chat months earlier (Aug 2026); the session itself had been used normally since.
  • Worked from plain sqlite inspection of opencode.db; no exotic config involved. Plugin list present from config defaults, not plugin-specific.

Activity

  1. opencodejr commented on Nov 5, 2026

    @opencodejr

    Thanks for the detailed report and the verified workaround!

    I think this is a duplicate of #50076. That issue reports the same V1 migration failed / RangeError: Out of memory on a session with a large inline base64 PDF, with the migration.v1-v2 cursor stuck and the same failure on every restart.

    Possible duplicates and related issues, ordered by likelihood:

    Your report adds useful information to #50076. A Linux reproduction attempt there couldn't trigger the OOM and guessed it might only affect macOS. Your case is on Linux x64 with 2.2.5, with about 900 MB across three file parts in a single message, so total session size may be what matters. Please consider adding these details to #50076.

    The bot will close this issue as a duplicate in 1 day unless you reply explaining how your problem is different.

  2. 0xAlok commented on Nov 5, 2026

    @0xAlok
    Author

    Confirmed duplicate of #50076. Cross-posted the Linux x64 reproduction details and the verified cursor workaround there (they correct the macOS-only hypothesis in that report). Closing this one as such.

  3. opencodejr commented on Nov 5, 2026

    @opencodejr

    Thanks for confirming, and for adding the Linux x64 reproduction and the cursor workaround to #50076. Further updates on this will be tracked there.

  4. opencodejr commented on Nov 6, 2026

    @opencodejr

    Closing this as a duplicate of #50076, as you confirmed. Thanks again for adding the Linux x64 reproduction and the cursor workaround there. Please follow #50076 for any updates on the V1→V2 migration running out of memory on large inline attachments.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions