Skip to content

Codex diff snapshots can accumulate in unbounded ingestion queues #12883

Description

@alexfertel

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:

  1. The Codex session runtime's provider event queue.
  2. The Codex adapter's runtime event queue.
  3. ProviderService's runtime event pub-sub buffer.
  4. ProviderRuntimeIngestion's diff worker.
  5. 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:

  1. Create a Codex turn/diff/updated notification with a generated large string in params.diff.
  2. Let CodexAdapter map it to a canonical runtime event.
  3. Delay checkpointStore.isGitRepository or otherwise pause the diff worker.
  4. Publish many distinct diff snapshots through ProviderService.streamEvents.
  5. Observe that diffWorker accepts all events and retains their full raw.payload.diff and payload.unifiedDiff strings.
  6. 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

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedfeature request acceptedbugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions