Repository navigation
Description
Description
A long-running opencode web process can sustain very high CPU and memory usage while an agent session is active. The behavior appears to worsen as session history and SQLite event data grow.
This is not only a UI rendering issue. The backend appears to repeatedly process and persist large session/event data during agent iterations.
Observed behavior
- OpenCode version:
1.18.15 - Command:
opencode web - Process lifetime: more than 2 days
- CPU usage: approximately 180–210% sustained
- RSS: approximately 1.5–1.7 GB
- Physical memory footprint: tens of GB
- SQLite database: approximately 66–71 GB
- The process was actively running an agent loop involving tool calls and other background work.
After stopping the process and inspecting the database:
- The
eventtable occupied approximately 39.7 GB. - Approximately 2,142 event rows were associated with one aggregate.
- Deleting sessions did not remove all associated event rows.
- Orphaned event rows remained because event records were not fully tied to session deletion.
- After removing orphaned events and vacuuming the database, the database reduced to approximately 248 KB.
Expected behavior
Opening or continuing a session should load only:
- Session metadata.
- The visible/recent message window.
- A bounded summary/context window.
Older messages, tool outputs, attachments, and event history should be loaded lazily only when explicitly requested.
CPU and memory usage should remain bounded as session history grows. Large historical data should not be repeatedly hydrated or serialized during every agent iteration.
Suspected causes
These are hypotheses requiring profiling:
- Full or oversized session history is hydrated during prompt-loop iterations or compaction.
- Large tool outputs and serialized event payloads are retained inline in SQLite.
- Event records are written at high frequency during tool execution.
- Session deletion does not transactionally remove all related event records.
- Large SQLite pages/cache/mmap activity amplifies memory pressure and serialization cost.
Suggested fixes
- Hydrate only the recent/required context for prompt execution.
- Lazy-load historical messages and collapsed tool output.
- Bound tool-output and event-payload sizes.
- Store large historical output externally or in compressed cold storage.
- Add transactional session/event deletion with orphan-event cleanup.
- Prevent deletion of active sessions.
- Add storage quotas and backpressure.
- Add profiling/telemetry for prompt hydration, event serialization, SQLite writes, and cache usage.
- Add a long-running soak test covering large tool output, compaction, child sessions, reconnects, and pruning.
Activity
github-actions commentedon Aug 31, 2026
This issue appears to overlap significantly with existing issues. Please check:
opencode web, orphaned event rows,Snapshot.diffFullpipeline writing unbounded data tomessagerows. This issue appears to be a broader framing of the same root cause already reported in bug: Snapshot.diffFull serialises full patch text into message rows, causing unbounded DB growth and web UI hang #46138.If this is intentionally a separate, higher-level tracking issue covering multiple root causes (DB growth, memory, lazy loading, orphan cleanup), please clarify that relationship to #46138 so maintainers can link them appropriately.
CryptArchy commentedon Sep 2, 2026
A data point on hypothesis 4, from a headless
opencode serveon 1.18.23 that deletes every session programmatically after its run.We see the same orphans you do — 509 of 2,065 aggregates in
event_sequencehad no matchingsessionrow: 127,081 events, 239 MB, in a 1.6 GB database.But the delete path itself works. Creating a session over the API and deleting it with
DELETE /session/:idremoves itseventandevent_sequencerows along with the session:So it isn't a missing cleanup path, it's one that doesn't always complete. #30814 notes that code swallows its exceptions, and #42538 reports deletes taking 10–30s and leaving ghost state; our deletes run on a deadline, which would race exactly that. There's also no database-level backstop:
event.aggregate_idreferencesevent_sequence(aggregate_id), notsession(id), so nothing cascades when the application path fails.Byte breakdown from the same database, via
dbstat—eventis 1,035 MB of the 1.6 GB, againstpart442 MB andmessage13 MB. By type:message.part.updated.1message.updated.1session.updated.1Lastly, in case it helps anyone considering a prune: we deleted every event row from a
.backupcopy and served it. The session list and session load were identical to a pristine copy of the same database — 76 sessions listed either way, and the same session returned 192 messages / 883 parts / 27 text parts. 1.59 GB → 467 MB after vacuum, withsession,messageandpartuntouched. On 1.18.23 only; that's an observation, not a guarantee about other versions.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 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊