Repository navigation
Automatic compaction double-counts prior reasoning against reported token usage and triggers unnecessary compaction #49026
Description
What version of Codex CLI is running?
Maintained Codex fork based on upstream commit 12de0e3
What subscription do you have?
ChatGPT Pro — $200/month
Which model were you using?
GPT-6 Astra Ultra
What platform is your computer?
Windows 10 x64
What terminal emulator and version are you using (if applicable)?
No response
Codex doctor report
What issue are you seeing?
Summary
Codex prematurely triggered automatic compaction because its context estimator added 72,772 estimated tokens for prior encrypted reasoning on top of a completed response's 177,413 reported tokens.
The resulting count was 250,193 tokens, crossing the 244,800 automatic-compaction threshold, even though the last reported usage remained 67,387 tokens below that threshold.
The incorrect threshold decision then caused a real additional remote compaction request consuming:
- 177,445 input tokens
- 176,768 cached input tokens (already included in input)
- 4,091 output tokens
- 181,536 total reported tokens
There was no manual compaction request.
Exact calculation
Immediately before compaction:
| Measurement | Tokens |
|---|---|
| Last response input | 176,904 |
| Cached input, included above | 173,952 |
| Last response output | 509 |
| Reasoning output, included above | 361 |
| Last reported total | 177,413 |
| Estimated earlier encrypted reasoning | 72,772 |
| Newly appended item estimate | 8 |
| Count used for automatic compaction | 250,193 |
| Automatic-compaction threshold | 244,800 |
| Effective context-window limit | 258,400 |
The decision was:
177,413 + 72,772 + 8 = 250,193
250,193 >= 244,800 -> automatic compaction
250,193 < 258,400 -> full context-window limit not reached
Reconstructing the recorded history reproduces the 250,193-token count exactly.
Root cause
The additional 72,772 tokens came from 148 encrypted reasoning items from earlier model responses retained in history.
When server_reasoning_included is false, Codex effectively calculates:
last reported total
+ estimated earlier reasoning
+ newly added items
The affected WebSocket handshake did not contain:
X-Reasoning-Included
so this branch was used.
The defect is that a historical subset is added to an already completed request's reported usage without establishing that the two quantities are disjoint.
The 148 reasoning items were not newly generated reasoning. They matched the archived preceding request input by hash.
Actual remote operation caused by the bug
| Operation | Input | Cached input | Output | Total |
|---|---|---|---|---|
| Response before compaction | 176,904 | 173,952 | 509 | 177,413 |
| Automatic compaction | 177,445 | 176,768 | 4,091 | 181,536 |
| First response afterward | 28,684 | 12,288 | 80 | 28,764 |
The compaction began immediately after the incorrect threshold decision.
This was not a UI-only accounting error. The incorrect context calculation directly caused an additional remote model operation.
Upstream code
The running fork is based on upstream commit:
12de0e395d3313bc564190d983cb4f5acf0be713
These functions were compared directly with that upstream commit and are unchanged:
codex-rs/core/src/context_manager/history.rs
get_non_last_reasoning_items_tokens
get_total_token_usage
is_user_turn_boundary
Relevant upstream code:
core/src/context_manager/history.rs
prior-reasoning estimate and total-token calculation
core/src/state/session.rs
server_reasoning_included state and history accounting
core/src/session/context_window.rs
automatic-compaction threshold comparison
core/src/session/turn.rs
server flag handling and compaction decision
codex-api/src/sse/responses.rs
X-Reasoning-Included handling
codex-api/src/endpoint/responses_websocket.rs
corresponding WebSocket handling
Correct accounting
For a settled request with a complete request-bound estimate, the context baseline should compare complete quantities rather than add historical reasoning to an already completed total:
baseline = max(
last server-reported input + output,
estimated complete composed input + actual output
)
next context estimate =
baseline + genuinely new local-item estimate
For this incident:
archived complete-input estimate: 201,228
developer-message normalization: -990
inserted text upper bound: +2,812
-------
complete composed-input bound: 203,050
Therefore:
max(177,413, 203,050 + 509) + 8
= 203,567
203,567 < 244,800
The correct decision does not trigger automatic compaction.
Related open issues
This sits within a much larger existing set of open Codex usage/accounting defects:
- #41220 — Abnormal Codex usage/quota depletion and usage-accounting inconsistencies — cross-report tracker
- #47825 — Hosted search usage causes premature auto-compaction
- #47003 — Auto-compaction request usage is never added to
total_token_usage - #45074 — Automatic compaction at 388K displayed tokens uses a 623K internal count
- #48919 — 36,318-token request followed by ~6% 5-hour depletion and ~2.95M daily tokens
- #46681 — Weekly usage dropped from ~75% to 16% in 19 minutes despite 98,700 reported tokens
- #44386 — ~150K cached input replayed per tool call, rapidly draining usage
- #38495 — Full-context polling loop consumed 34.6M tokens after work had completed
- #35463 — Subagent history/accounting behavior associated with an entire weekly quota drain
- #32640 — Repeated wait/resampling behavior causing massive token consumption
Customer impact
I pay $200/month for ChatGPT Pro, and I now maintain both my own Codex fork and an increasingly substantial local instrumentation and patching layer around it.
I find the current situation completely absurd: I am spending a meaningful portion of my paid OpenAI allowance using OpenAI's own models to identify, diagnose, and patch defects in OpenAI's own Codex client that themselves waste that allowance.
This bug is a concrete example. An incorrect client-side accounting decision caused an additional 181,536-token remote operation that should not have occurred at that point.
This is also not an isolated class of problem. The issue tracker already contains a substantial number of open reports involving incorrect accounting, unnecessary context replay, runaway polling, premature compaction, background activity, and subscription usage that does not reconcile with observable work.
A normal client bug wastes time.
A client bug that causes unnecessary inference consumes the product the customer paid for.
At $200/month, I should not have to spend an increasing portion of that same finite allowance maintaining a private patch layer around the official client simply to prevent avoidable usage consumption.
What steps can reproduce the bug?
-
Use a Codex session containing retained encrypted reasoning items from earlier model responses.
-
Reach a completed response where the server-reported token total remains below the automatic-compaction threshold.
-
Have
server_reasoning_included == false, as occurs when the response does not containX-Reasoning-Included. -
Append a new history item after that completed response.
-
Allow the normal Codex history-accounting path to evaluate the automatic-compaction threshold.
In the captured incident:
last reported total: 177,413
estimated prior reasoning: 72,772
new-item estimate: 8
-------
calculated context count: 250,193
automatic-compaction threshold: 244,800
context-window limit: 258,400
Codex therefore triggers automatic compaction even though the last reported total remains 67,387 tokens below the threshold.
The 72,772-token component is produced from the 148 retained encrypted reasoning items selected by the history estimator.
Reconstructing the recorded history reproduces the final 250,193-token count exactly.
The resulting automatic compaction produces an additional remote request:
input_tokens: 177,445
cached_input_tokens: 176,768
output_tokens: 4,091
total_tokens: 181,536
Relevant unchanged upstream functions:
codex-rs/core/src/context_manager/history.rs
get_non_last_reasoning_items_tokens
get_total_token_usage
is_user_turn_boundary
Upstream base:
12de0e395d3313bc564190d983cb4f5acf0be713
What is the expected behavior?
No response
Additional information
No response
Activity
Turingfly commentedon Sep 29, 2026
Independent corroboration on official Codex CLI 0.158.0/macOS, GPT-6 Astra, reasoning.context=all_turns, captured through claude-tap HTTP/SSE.
The server attributes 13,454 input tokens to 70 historical encrypted reasoning items before the latest instruction boundary (including AgentMessage boundaries). They have empty summaries and no plaintext content. Summing ALL item input attribution exactly equals the reported 250,478 input tokens. In 56/59 matched items, replayed input equals the original reasoning output plus two tokens, so this is not merely envelope overhead.
The captured response has no x-reasoning-included header. Native logs exactly match:
251,211 server total + 23,841 estimated historical reasoning + 653 new tool-result estimate = 275,705 client context count.
This verifies that already-counted historical reasoning is added again as an estimate. The 0.158.0 history.rs fallback matches the calculation. This request stayed below its compaction threshold: it proves the accounting overlap, not an additional compaction. The capture does not identify which upstream component caused the missing signal.
Please validate the reasoning-included contract for both SSE and WebSocket responses. Related: #39767; #32483 concerns the separate pre-turn reset path.
coinman-dev commentedon Oct 4, 2026
Same cause here on Codex 0.159.2 with
gpt-6.1-solunder WSL2. A small client patch that stops the double count is described in #39767 (comment): with it the same chat went from being compacted at 734,065 tokens to 922,856 with no early compaction, and its regression test fails on unpatched 0.160.0. Patch and notes: https://github.com/coinman-dev/codex-context-accounting-fixZhuchen00123 commentedon Oct 11, 2026
Synthetic-only corroboration of the native Responses Lite / all-turns accounting overlap. This comment contains no private rollout data, local configuration, account identifiers, or installed-client environment details.
Minimal protocol observation
Using
gpt-6.1-sol, I generated a fresh encrypted reasoning item with:The generation requested
reasoning.effort="high",reasoning.context="all_turns", andinclude=["reasoning.encrypted_content"].I then compared two small HTTP/SSE requests to the official Codex Responses endpoint, with Responses Lite enabled,
reasoning.context="all_turns", and low reasoning effort:Reply OK..summary: []and no plaintext content, followed by the same user message.Both requests returned HTTP 200 and
response.completed. Neither observed response containedx-reasoning-included.The replayed item's 41 input tokens exactly explain the input delta (
50 - 9). This establishes that the observed route counts historical encrypted reasoning in reported input usage without the compatibility header. The estimate is:For this composed history, the false-flag fallback would therefore add a 193-token estimate to the already inclusive 55-token reported total. That addition is overlapping accounting; the encrypted-length estimate also differs substantially from the actual item attribution. This arithmetic is a source-based implication, not an assertion that a desktop compaction was triggered by this tiny protocol sample.
Public upstream source
The relevant paths remain in upstream
maininspected at8c0f7011674145da7e10627769fb221c74fbf7a0:This complements #39767's all-turns observations and #32483's pre-turn ordering issue. The synthetic results do not identify which upstream component omitted the header and should not be extrapolated to every provider.
Could maintainers confirm the native Responses Lite / all-turns usage contract? If earlier reasoning is included in reported usage, compaction should avoid adding its estimate again. The accounting contract should stay associated with the corresponding successful usage, including across pre-turn checks, failed/cancelled streams, and resume/fork.