Codex budget draining drastically since 2026-06-25: needs_follow_up loop + rate_limits.used_percent no longer reported

Open 💬 1 comment Opened Jun 25, 2026 by zhulijin1991

What issue are you seeing?

Starting around 2026-06-25, my Codex budget drains drastically faster than before on my $200/mo Pro subscription. For example, a single prompt ran for ~6 minutes and consumed tokens as if it were many prompts, with model_needs_follow_up=true looping repeatedly inside one turn. The 5h/weekly quota is gone within a handful of prompts.

Two concrete anomalies surfaced in the local log DB (~/.codex/logs_2.sqlite, table logs):

Anomaly 1 — rate_limits.used_percent stopped being reported

The structured rate_limits payload (with primary.used_percent / secondary.used_percent, window_minutes: 300 / 10080) that the server used to return disappeared after 2026-06-23. Per-day count of log rows containing used_percent:

| Date | Rows with used_percent |
|------|--------------------------|
| 2026-06-17 | 7 |
| 2026-06-18 | 9 |
| 2026-06-19 | 15 |
| 2026-06-20 | 16 |
| 2026-06-21 | 18 |
| 2026-06-22 | 24 |
| 2026-06-23 | 1 |
| 2026-06-24 | 0 |
| 2026-06-25 | 0 |

So there is currently no per-request primary/secondary usage feedback at all, while the budget itself drains fast — making it impossible to self-monitor.

Anomaly 2 — single turns loop on needs_follow_up=true, burning tokens

On a normal day (2026-06-17), a turn logged 1–3 post sampling token usage samples and the total_usage_tokens delta within a turn was small (e.g. 274, 1190). The 5h primary.used_percent moved gently: 4% → 7% → 8% → 18% → 21% across the day.

On 2026-06-25, 53 distinct turns produced 248 sampling rows, and all 53 turns logged model_needs_follow_up=true at least once (195 true samples vs 53 false terminations). 18+ turns hit ≥5 consecutive follow-up samples. Worst cases:

| Turn (redacted) | Samples | Duration | total_usage_tokens (min→max) | Delta |
|-----------------|:-------:|:--------:|:------------------------------:|:-----:|
| Turn A | 23 | 6m39s | 52,643 → 174,494 | +121,851 |
| Turn B | 22 | 8m00s | 63,223 → 177,705 | +114,482 |
| Turn C | 17 | 2m45s | 100,642 → 136,349 | +35,707 |
| Turn D | 15 | 2m39s | 127,175 → 174,340 | +47,165 |
| Turn E | 6 | 2m02s | 47,005 → 246,323 | +199,318 |

Timeline of Turn A (one prompt, medium reasoning effort, gpt-5.4), total_usage_tokens sampled every few seconds with needs_follow_up=true the whole way until the final sample:

10:02:01  52,643   needs_follow_up=true   token_limit_reached=false
10:02:11  75,811   needs_follow_up=true   token_limit_reached=false
10:02:20  96,564   needs_follow_up=true   token_limit_reached=false
10:02:29 108,338   needs_follow_up=true   token_limit_reached=false
10:02:41 116,197   needs_follow_up=true   token_limit_reached=false
10:03:08 120,191   needs_follow_up=true   token_limit_reached=false
10:03:33 133,647   needs_follow_up=true   token_limit_reached=false
10:04:35 139,440   needs_follow_up=true   token_limit_reached=false
10:04:52 140,543   needs_follow_up=true   token_limit_reached=false
10:05:15 142,253   needs_follow_up=true   token_limit_reached=false
10:05:28 143,108   needs_follow_up=true   token_limit_reached=false
10:06:09 146,222   needs_follow_up=true   token_limit_reached=false
10:06:32 147,421   needs_follow_up=true   token_limit_reached=false
10:07:01 149,644   needs_follow_up=true   token_limit_reached=false
10:07:10 158,442   needs_follow_up=true   token_limit_reached=false
10:07:21 159,801   needs_follow_up=true   token_limit_reached=false
10:07:55 162,505   needs_follow_up=true   token_limit_reached=false
10:08:03 169,054   needs_follow_up=true   token_limit_reached=false
10:08:08 171,675   needs_follow_up=true   token_limit_reached=false
10:08:21 172,491   needs_follow_up=true   token_limit_reached=false
10:08:27 173,597   needs_follow_up=true   token_limit_reached=false
10:08:32 174,494   needs_follow_up=true   token_limit_reached=false
10:08:40 174,494   needs_follow_up=false  token_limit_reached=false

This looks like a needs_follow_up loop: the model keeps requesting follow-up within a single turn, tokens accumulate monotonically, and only a false terminates it — by which point one prompt has cost ~122k–199k tokens. This matches the community reports in #28879 (per-token limit cost up 10–20× since ~June 16) and #14593.

What steps can reproduce the bug?

  1. Codex desktop app, macOS arm64, gpt-5.4 / gpt-5.5, default service tier, reasoning effort medium/xhigh.
  2. Send a reasonably simple prompt.
  3. Observe in ~/.codex/logs_2.sqlite (target='codex_core::session::turn') that the turn logs many consecutive model_needs_follow_up=true samples with monotonically increasing total_usage_tokens, and that no rate_limits.used_percent payload is returned anymore.

No fast mode, no multi_agent, no large-context flag, no manual feature flags.

What is the expected behavior?

  • Per-request rate_limits (primary/secondary used_percent) should be reported consistently, as it was through 2026-06-23, so users can monitor 5h/weekly usage.
  • A single turn should not loop on needs_follow_up=true for minutes, accumulating 100k–200k tokens for one prompt. Token consumption per prompt should be in the same ballpark as before 2026-06-24.

Additional information

  • Subscription: Pro ($200/mo).
  • Log DB: ~/.codex/logs_2.sqlite table logs, target='codex_core::session::turn' and target='codex_api::endpoint::responses_websocket'.
  • All turn/thread IDs and local file paths in this report have been redacted.
  • Related: #28879, #14593.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗