Skip to content

[Bug]: Native Codex resume appends duplicate T3 application instructions to model input #25191

Open

Description

@sharno

Before submitting

  • I searched existing issues and did not find a native Codex duplicate.
  • I included source-level diagnosis and a local mock-model reproduction.

Area

apps/server — native Codex adapter

Problem

Resuming a native Codex conversation after recreating its runtime appends another copy of unchanged T3 application instructions. Earlier instruction messages remain in the conversation. This is model-visible duplication, not just repeated RPC parameters or a transcript display artifact.

The underlying mechanism is in Codex's in-memory AdditionalContextStore; T3's per-turn additionalContext delivery exposes it. This report does not establish a subscription-usage multiplier or claim that every normal turn duplicates instructions.

Versions and environment

  • Serving desktop package: T3 Code 0.1.2, build commit 044f072 (from installed app.asar package metadata).
  • Native provider: Codex app-server 0.164.0, Linux x86_64 / NixOS.
  • Observed in a normal GPT-6.1-Sol conversation; independently reproduced against the installed Codex binary using an isolated home and a loopback mock Responses endpoint, without paid inference.
  • Corresponding T3 code is also present in main at e856833.

Steps to reproduce

  1. Start codex app-server with a fresh temporary CODEX_HOME, a custom local mock Responses provider, and no real credentials.
  2. Initialize with capabilities.experimentalApi: true, then thread/start.
  3. Run two completed turn/start requests against the same thread with identical application context:
{
  "threadId": "<thread-id>",
  "input": [{"type": "text", "text": "Say hi"}],
  "additionalContext": {
    "t3_code_repro": {
      "kind": "application",
      "value": "UNIQUE_T3_CONTEXT_MARKER"
    }
  }
}
  1. Stop that isolated app-server, start another against the same temporary home, initialize, and call thread/resume with the original thread ID and excludeTurns: true (matching T3).
  2. Run two more completed turns with the identical context.
  3. Capture each outgoing /v1/responses request and count developer messages in input containing the marker.

A local Rust JSON-RPC driver plus HTTP/SSE mock executed this sequence. The mock returns response.created, an assistant response.output_item.done, and response.completed for each request. The driver and captured requests were kept local; raw production logs were not uploaded.

Expected behavior

Unchanged T3 context already present in retained history should not become a second developer message merely because the native runtime was recreated and the conversation resumed. Legitimate refreshes after context changes or compaction must remain supported.

Actual behavior

Captured outgoing model input:

turn 1: 1 model-visible copies
turn 2: 1 model-visible copies
[restart isolated app-server; thread/resume]
turn 3: 2 model-visible copies
turn 4: 2 model-visible copies

The real T3 protocol log and native rollout agree:

2026-10-25T21:47:08Z  turn/start     seven T3 additionalContext entries
2026-10-25T21:47:09Z  rollout        first set of seven developer messages
2026-10-26T01:28:01Z  thread/resume  same native conversation
2026-10-26T01:28:01Z  turn/start     same seven additionalContext entries
2026-10-26T01:28:02Z  rollout        second, byte-identical set of seven messages
[later turns send the same map without appending further copies]

The duplicated set is t3_code_orchestration_01 through _05, t3_code_runtime, and t3_code_tools: 11,927 characters per set in this conversation. No compaction or thread/inject_items occurred on the observed duplication path.

Source-level root cause

T3, pinned to the installed build:

Codex, pinned to the 0.164.0 release commit 6aaac694803004a63c6f393d153c96d22521a6b9:

The natural underlying fix is to persist/restore Codex's keyed context state consistently with retained history, or provide a supported resume-time seeding mechanism. A T3 workaround would need to preserve context updates and compaction behavior; simply deleting per-turn context is not a complete fix.

Impact and validation limits

Extra instruction copies consume model context and introduce new input after runtime reconstruction. The mock verifies actual model input; it does not quantify quota charging, cache-hit changes, or explain every reported usage discrepancy. Production evidence establishes one resume-triggered duplicate; repeated-cycle accumulation was not separately tested.

Related: #24465 and #24185 concern ACP instruction delivery and use different provider code paths. This report is native Codex, not Copilot ACP.

Investigation and report prepared with Codex GPT-6.1-Sol through T3 Code.

Activity

  1. DKLokeswaran commented on Nov 2, 2026

    @DKLokeswaran

    Confirming this resume path can hard-fail, not just duplicate.

    Setup (custom): opencode-go/muse-spark-1.3-contributor driven through the Codex harness via opencodex (ocx). I know that's not a supported matrix — mentioning it because it makes the underlying resume bug visible as a rejection.

    • T3 server 0.1.7-nightly.20261102.4066 on dkl-home-server (linux x64, systemd service on :3773), web client
    • Codex app-server 0.166.0, modelProvider: openai
    • Thread: 1 completed Codex turn, then follow-up after thread/resume(excludeTurns:true)

    What happened: first turn (short history + 5 kind:application entries: t3_code_orchestration_01/02/03, t3_code_runtime, t3_code_tools) succeeded. Resumed second turn with the same 5 entries failed in ~2s:

    {"model":"muse-spark-1.3-contributor","error":{"code":null,"message":"`input[14]` did not match any supported type","param":"input[14]","type":"invalid_request_error"}}

    Provider log shows thread/resume → turn/start with the identical additionalContext map. Switching the same thread to the opencode harness with the identical model string succeeded.

    Reading: matches this issue's root cause (resume doesn't rebuild AdditionalContextStore, next turn re-merges unchanged context). Stock models tolerate it as duplication; this stricter endpoint rejects the re-appended item at input[14]. Fix here should also fix this symptom.

    Happy to pull fuller redacted protocol excerpts if useful. Investigated with Muse Spark via t3 triage.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions