What happened
Large Codex turn/diff/updated snapshots remain attached to runtime events after provider event logging has bounded its output. T3 then sends each full event through unbounded queues before it records a small placeholder checkpoint.
The ingestion code never reads payload.unifiedDiff. It uses only event identity, thread and turn identity, and time. Keeping the full diff in these queues adds memory use without changing behavior.
This source audit followed repeated desktop backend V8 out-of-memory failures. The logs do not record queue depth, so this report does not claim that queue growth caused those failures. It reports a reachable, byte-unbounded path found during that investigation.
Diagnosis
CodexAdapter maps a native turn/diff/updated notification to a canonical event that retains the diff in two fields:
raw.payload.diff
payload.unifiedDiff
The event then passes through these unbounded buffers:
- The Codex session runtime's provider event queue.
- The Codex adapter's runtime event queue.
ProviderService's runtime event pub-sub buffer.
ProviderRuntimeIngestion's diff worker.
- The ingestion lifecycle worker after repository detection succeeds.
makeDrainableWorker uses TxQueue.unbounded. The diff worker can wait for repository and VCS checks, so its consumer can run more slowly than the provider event source.
recordProviderDiff does not read unifiedDiff. It creates a placeholder checkpoint with status: "missing" and files: []. The full string can be removed before the first queue, or at least before diffWorker.enqueue.
Provider logs from the affected install contain 3,017 canonical diff notifications after the logger fix, with a peak of 57 notifications in one minute. The logger records are bounded, so they do not reveal the original diff sizes. Separate read-only database checks found individual decoded Codex file-change diffs near 49 million characters, which confirms that this provider can produce large diff strings on this install.
Expected behavior: T3 should not retain unused full diff strings in unbounded queues. It should reduce the event to the fields required for checkpoint scheduling, coalesce repeated snapshots for the same thread and turn, or apply bounded backpressure.
Steps to reproduce
Source-level reproduction:
- Create a Codex
turn/diff/updated notification with a generated large string in params.diff.
- Let
CodexAdapter map it to a canonical runtime event.
- Delay
checkpointStore.isGitRepository or otherwise pause the diff worker.
- Publish many distinct diff snapshots through
ProviderService.streamEvents.
- Observe that
diffWorker accepts all events and retains their full raw.payload.diff and payload.unifiedDiff strings.
- Resume the worker and observe that downstream checkpoint handling never reads either diff field.
A regression test can use a small configured queue or generated strings. It should assert that queued diff work does not retain unifiedDiff and that repeated updates for one thread and turn stay bounded.
Version
0.0.43-nightly.20260920.2005, commit 7445aa733ada.
Environment
- T3 Code desktop app with local backend
- Darwin 25.6.0, arm64
- Node.js 26.8.2
- Codex provider
Evidence
Post-fix canonical diff notifications: 3,017
Peak canonical diff notifications: 57 per minute
Largest decoded file-change diff seen: about 49 million characters
Queue declarations:
CodexSessionRuntime: Queue.unbounded<ProviderEvent>()
CodexAdapter: Queue.unbounded<ProviderRuntimeEvent>()
ProviderService: PubSub.unbounded<ProviderRuntimeEvent>()
DrainableWorker: TxQueue.unbounded<A>()
Ingestion route:
turn.diff.updated -> diffWorker.enqueue(event)
repository check -> worker.enqueue({ source: "diff", event })
recordProviderDiff(event) -> placeholder checkpoint
No home paths, thread IDs, turn IDs, project names, commands, diff text, or credentials are included.
Related issues
No open issue or pull request found in the upstream search covers unused full diffs in the ingestion queues.
Fix applied or workaround
No source patch or state change was made. Restarting the backend clears queued events but does not prevent the path from recurring.
Filed by
Codex, GPT-5, via t3 triage
What happened
Large Codex
turn/diff/updatedsnapshots remain attached to runtime events after provider event logging has bounded its output. T3 then sends each full event through unbounded queues before it records a small placeholder checkpoint.The ingestion code never reads
payload.unifiedDiff. It uses only event identity, thread and turn identity, and time. Keeping the full diff in these queues adds memory use without changing behavior.This source audit followed repeated desktop backend V8 out-of-memory failures. The logs do not record queue depth, so this report does not claim that queue growth caused those failures. It reports a reachable, byte-unbounded path found during that investigation.
Diagnosis
CodexAdaptermaps a nativeturn/diff/updatednotification to a canonical event that retains the diff in two fields:raw.payload.diffpayload.unifiedDiffThe event then passes through these unbounded buffers:
ProviderService's runtime event pub-sub buffer.ProviderRuntimeIngestion's diff worker.makeDrainableWorkerusesTxQueue.unbounded. The diff worker can wait for repository and VCS checks, so its consumer can run more slowly than the provider event source.recordProviderDiffdoes not readunifiedDiff. It creates a placeholder checkpoint withstatus: "missing"andfiles: []. The full string can be removed before the first queue, or at least beforediffWorker.enqueue.Provider logs from the affected install contain 3,017 canonical diff notifications after the logger fix, with a peak of 57 notifications in one minute. The logger records are bounded, so they do not reveal the original diff sizes. Separate read-only database checks found individual decoded Codex file-change diffs near 49 million characters, which confirms that this provider can produce large diff strings on this install.
Expected behavior: T3 should not retain unused full diff strings in unbounded queues. It should reduce the event to the fields required for checkpoint scheduling, coalesce repeated snapshots for the same thread and turn, or apply bounded backpressure.
Steps to reproduce
Source-level reproduction:
turn/diff/updatednotification with a generated large string inparams.diff.CodexAdaptermap it to a canonical runtime event.checkpointStore.isGitRepositoryor otherwise pause the diff worker.ProviderService.streamEvents.diffWorkeraccepts all events and retains their fullraw.payload.diffandpayload.unifiedDiffstrings.A regression test can use a small configured queue or generated strings. It should assert that queued diff work does not retain
unifiedDiffand that repeated updates for one thread and turn stay bounded.Version
0.0.43-nightly.20260920.2005, commit7445aa733ada.Environment
Evidence
Related issues
No open issue or pull request found in the upstream search covers unused full diffs in the ingestion queues.
Fix applied or workaround
No source patch or state change was made. Restarting the backend clears queued events but does not prevent the path from recurring.
Filed by
Codex, GPT-5, via
t3 triage