Repository navigation
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/desktop
Steps to reproduce
- Launch T3 Code (Nightly) with a
statev2.sqlitethat contains multi-subagent orchestration history (any provider). - Run a single active Codex session — subagents are not required, one plain session is enough.
- Within minutes (or seconds after a relaunch) the desktop main process aborts with SIGABRT.
Escalation observed while manually purging orchestration_events between attempts:
| DB size at crash | outcome |
|---|---|
| 24 GiB | 3 crashes: 4 h uptime, then relaunch 48 s, then 39 min |
| 13 GiB | crash after 19 min |
| 4.8 GiB | crash loop: 7 s and 33 s after launch |
Crash timing tracks provider activity, not DB size alone: the 09:25:01/09:25:23 crashes landed one second after the last thread:provider:codex:native-thread:* events were written.
Expected behavior
The app stays running; thread reads are byte-bounded or streamed so any database state can be materialized without exhausting the V8 heap.
Actual behavior
The desktop main process aborts with V8 FatalProcessOutOfMemory inside node:sqlite row materialization. 8 SIGABRT crashes in 12 hours (macOS .ips reports). Every crash marks all in-flight delegated tasks cancelled (partial outputs left on disk), and the "N delegated tasks stopped" notification was delivered twice per incident, each delivery spawning a duplicate orchestrator run.
Impact
Major degradation or frequent failure
Version or commit
0.0.46-nightly.20261010.2922
Environment
macOS 26 (arm64), T3 Code (Nightly) desktop app. Workload: multi-subagent orchestration via opencode (cline-pass) and Codex native subagents; later reproduced with a single Codex session and no subagents.
Logs or stack traces
node::sqlite::StatementSync::All → StatementExecutionHelper::All
→ ExtractRowValues → StatementExecutionHelper::ColumnToValue
→ v8::String::NewFromUtf8 / v8::String::NewFromOneByte / v8::Array::New (allocation point varies)
→ node::OOMErrorHandler → abort() (SIGABRT)
Crash report timestamps: 21:21:15, 21:21:58 (relaunch, 48 s), 22:01:15 (39 min), 09:08:33 (19 min), 09:25:01 + 09:25:23 (relaunch, 27 s), 09:34:17 + 09:34:43 (relaunch, 7 s).
Supporting data
Read-only dbstat scan of statev2.sqlite before cleanup: 24 GiB / 6.33M pages; orchestration_events = 19.0 GiB table + ~3.6 GiB indexes.
| event_type | rows | payload | note |
|---|---|---|---|
node.updated |
5,471,857 | 8.42 GiB | per-streaming-chunk, unbatched |
turn-item.updated |
749,867 | 3.37 GiB | full snapshot per update, single payloads up to 2.2 MB |
projection_thread_activities |
— | 59 MB | projection side is trimmed; the durable stream is the unbounded side |
Manual purge to 4.8 GiB did not stop the crash loop, so the read path needs a byte budget independent of database size.
Workaround
Quit the app, delete aged thread events from orchestration_events, then VACUUM INTO a compact copy and swap. Mitigates frequency only; the unbounded writers re-grow the table.
Fix request
- Byte-budget (or stream) thread reads in the desktop main process — never materialize an unbounded result set.
- Bound/compact the
orchestration_eventswriters (node.updated,turn-item.updated, Codex native-thread activity) and add retention. - Delegated tasks should survive an app restart instead of being marked
cancelled.
Related: #12758 (Codex diff duplication — likely one contributor), #12584 (same V8-OOM abort class on the background service), #14701 (large-DB reads blocking the event loop).
Activity
YL404 commentedon Oct 11, 2026
Closing per reporter confirmation: a newer nightly includes retention fixes that address this (notably #17784 in 0.0.46-nightly.20261011.2967 — thread loads no longer retain every OpenCode message; related read-path hardening in #17785 is still in review). If OOM aborts recur on 0.0.46-nightly.20261011.2967 or later, please reopen with a fresh .ips.