Repository navigation
Description
Summary
On OpenCode v2.0.20, opencode.db grew to 587 MB while holding only 3.7k messages. 538 MB (92%) of the file was base64 image data embedded directly in session_message.data from image tool results (content[].uri = "data:image/png;base64,..."). Deleting the offending session with the official CLI freed 428 MB of pages but did not shrink the file; a manual VACUUM reduced it to 158 MB.
Environment
- opencode version:
2.0.20(@opencode/cli) - OS: Windows 11 26H2, build 26300.9711, x64 (reported by
CurrentVersionas "Windows 10 Pro") - Terminal: Windows Terminal,
TERM=xterm-256color,TERM_PROGRAMunset - Shell: PowerShell 7.6.6 (
pwsh) - Install/channel: npm global install of
@opencode/cli@2.0.20 - Active plugins: none (global
opencode.jsonchas noplugin/pluginsentries)
Reproduction
- In a single session, use the
imagetool repeatedly (about 50 calls, 4 images per assistant turn). - Observe each tool result stored in the session history:
{ "type": "file", "uri": "data:image/png;base64,<1–5 MB>", "mime": "image/png" } - After a ~6 hour session: 550 messages, 421 MB of
session_message.data, DB file 587 MB. - Delete the session officially:
opencode session delete <sessionID>→ succeeds, 428.6 MB becomes freelist pages, file size unchanged at 587 MB. - Run
VACUUMexternally → 157.5 MB,PRAGMA quick_check=ok.
Expected Behavior
- Media produced by tools should be stored as files (or at minimum as BLOBs) and referenced from the message, not inlined as base64 inside JSON history.
- Disk usage should be proportional to text conversation size; 3.7k messages should not approach 600 MB.
- After deleting a session, the reclaimed space should be returned to the filesystem (automatic
VACUUM/auto_vacuum=INCREMENTAL), or a documented maintenance command should exist.
Actual Behavior
Measured read-only (file:...?mode=ro, no writes):
| Table / column | Size | Rows |
|---|---|---|
session_message.data |
567.0 MB | 3,697 |
kv.value |
6.3 MB | 2 |
instruction_blob |
0.24 MB | 65 |
Message type breakdown:
| type | size | rows |
|---|---|---|
| assistant | 562.4 MB | 2,982 |
| compaction | 2.2 MB | 29 |
| user | 1.3 MB | 210 |
| system | 1.0 MB | 93 |
Image payload:
- 298 embedded
data:image/*URIs = 538.2 MB of base64 text (PNG 524.6 MB, JPEG 13.6 MB) - Largest single message: 14.93 MB, containing four
imagetool results of 3.2–4.8 MB each - One session accounted for 199 images / 418.3 MB
- After deleting that session, 99 images / 119.9 MB remain in other sessions
Database facts: page_size=4096, page_count=150304, freelist_count=1597 before deletion (only 6.2 MB free), auto_vacuum=0, WAL mode. After session delete, freelist_count grew to ~104k pages (428.6 MB) but the file stayed at 587 MB.
Base64 inflates the binary payload by ~33% (403.6 MB of PNG/JPEG → 538.2 MB of text), and the same data is also materialized as a JSON string for every read of the message.
Additional Context
Workaround used, in this order:
opencode session delete ses_...
# file unchanged: 587 MB, 428.6 MB freelist
python vacuum.py # PRAGMA wal_checkpoint(TRUNCATE); VACUUM; quick_check
# 587 MB -> 157.5 MB, quick_check ok, ~4.6 sSuggestions:
- Store
filetool outputs in the existing storage layer (content-addressed files), keeping only a path/hash in the message JSON. - Add a migration/maintenance path that strips existing base64 payloads from history (text-only history stays readable).
- Enable
auto_vacuum=INCREMENTALfor new databases, and/or addopencode db vacuum(with a--dry-run) so users do not need external SQLite tooling. - Consider deduplicating identical images (the same generated image can appear in multiple messages/retries).
Related reports: #58124 (DB always growing), #55282 (687 MB DB, 99.5% free pages in another store), #56058 (RSS growth with image attachments), PRs #56365 (reclaim free pages) and #57882 (inactive session expiry). This report adds a distinct payload source: image tool outputs inlined as base64 in session_message.data, which is 92% of the file in this case.
Activity
opencode-agent commentedon Oct 23, 2026
Thanks for the detailed measurements, @MercuryOoO. As I understand it, you're asking for two things: tool-produced media (such as
imagetool results) should be stored outsidesession_message.dataand referenced from the message instead of inlined as base64, and the database should give freed space back to the filesystem, either through incremental auto-vacuum or a documentedvacuumcommand. On current v2, media is stored inline and nothing vacuums the database, so this is a storage improvement rather than a regression. Maintainers will consider it.Related work you may want to follow or link to:
media_blobtable, storesblob:sha256:references, removes duplicate images and adds an offline migration script. This covers your suggestions 1, 2 and 4.readtool storing the full data URI in tool-result records.Until then, your workaround (
opencode session deletefollowed by an offlineVACUUMwith the service stopped) is the way to reclaim the space.