Skip to content

codex: context meter reads the WS-chained total, so full-history resends blow past the window at "35% ctx" and the session wedges (no auto-compact) #174

Description

@justrach

Symptom (live session, gpt-5.6-sol @ Extra high, v0.0.200)

codex api error: Your input exceeds the context window of this model. Please adjust your input and try again.
[gpt-5.6-sol · Extra high · codex · 95k/270k ctx (35% · compact@216k)]

The meter says 95k/270k, compaction is armed at 216k — yet the backend rejects for exceeding the window, and every retry fails the same way (the session is wedged until a manual /compact or /clear).

Root cause — the meter and the wire measure different things

  1. stepResponses appends every output item to local history, including each turn's encrypted reasoning item (agent_steps.zig:42); buildBody replays them with store:false + include: [reasoning.encrypted_content].
  2. Within a turn, the WS delta transport chains with previous_response_id; the server discards prior-turn reasoning from chained context, so usage.total_tokens tracks a pruned conversation. recordUsageResponses copies that number into last_context_tokens (agent_request.zig) — this is the 95k.
  3. runTurn closes the WS per turn (agent.zig:299), so the first request of every turn is a full-history resend — local history, all accumulated reasoning items included. At Extra-high effort on a long session that true cost can be ~3x the chained number.
  4. The full resend is precisely the request the backend rejects — so no corrective usage ever arrives, last_context_tokens stays at the stale chained value, the pre-turn compact check (mainloop.zig:705) and the ApiError recovery (mainloop.zig:654) both see 95k < 216k, and nothing compacts. Wedged.

Why codex CLI doesn't hit this

Its ContextManager.get_total_token_usage() doesn't trust the last server report alone: it adds an estimate for reasoning items the server accounting excluded (get_non_last_reasoning_items_tokens() when server_reasoning_included is false) plus items appended since the last report, and compaction gates on that corrected number. Confirmed against openai/codex codex-rs/core/src/context_manager/history.rs.

Fix (parity + belt-and-braces)

  1. Meter correction: last_context_tokens = max(server total, full-input estimate) on the responses path — estimate = serialized bytes of the full input array / 4 via a counting discard writer (zero-alloc). Makes compact@80% fire before the wall even while WS deltas keep server totals small.
  2. Context-rejection recovery: when the codex error contains "exceeds the context window", pin last_context_tokens to the window so the existing ApiError compact-and-recover path actually engages — rescues already-wedged sessions on their next input.
  3. Reasoning-aware trim: before the compaction summary request on the responses path, drop reasoning items older than the last user message (exactly the items the server itself discards in chains) so the compaction request itself fits under the wall it's trying to escape.

Related: #165 (transport divergence — this is the accounting consequence of the delta transport).

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions