Codex Desktop repeatedly re-enters the model during wait/status polling, consuming substantial credits

Open 💬 19 comments Opened Jul 24, 2026 by dimasyankauskas
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Summary

During intentional Ultra and multi-agent work, Codex Desktop repeatedly re-entered the model merely to wait for agents or poll terminal status. In a corrected reset-to-49%-usage window, model turns whose only tool action was wait/status polling accounted for 19.8% of raw local token volume.

Ultra and multi-agent use were intentional. The suspected defect is not normal agent fan-out; it is repeated model inference during quiet coordination and process-wait intervals, often at 30- or 60-second cadence.

Related reports exist, but this report adds current-build evidence, replay-safe accounting, a full usage-cycle window, tool-level decomposition, a representative long-running trace, and a public-rate-card estimate.

Environment

  • ChatGPT Pro
  • macOS on Apple silicon
  • Codex Desktop 26.721.31836 (build 5828)
  • Incident runtime: codex-cli 0.146.0-alpha.3.1
  • Mostly GPT-5.6 Sol

What issue are you seeing?

Long-running multi-agent tasks can generate hundreds of distinct model turns that do no substantive work beyond calling one of these tools:

  • wait_agent
  • list_agents
  • write_stdin
  • generic wait

Each polling interval appears to re-enter the model, ingest a large cached conversation prefix, and then make another wait/status call. This can continue for hours while delegated work or a terminal process is still running.

Corrected local evidence

I analyzed local rollout telemetry from one complete weekly reset window, from the reset through the first observed 49% usage state.

The parser:

  • excludes copied parent-history prefixes in child rollouts;
  • starts each child at its live task_started boundary;
  • ignores unchanged cumulative token snapshots; and
  • counts only genuine per-turn token deltas.

Corrected window:

  • 10,586 genuine model turns
  • 1,382,757,012 raw local tokens
  • 43,668,351 uncached input tokens
  • 1,335,723,264 cached input tokens
  • 3,365,397 output tokens
  • 96.83% cached-input share
  • 94,241 copied-prefix token records excluded
  • 404 duplicate token snapshots excluded

Tool-only wait/status turns:

| Tool group | Turns | Raw local tokens | Uncached input | Cached input | Output | Share of all raw |
|---|---:|---:|---:|---:|---:|---:|
| wait_agent / list_agents only | 985 | 142,137,652 | 2,654,345 | 139,365,376 | 117,931 | 10.3% |
| write_stdin / wait only | 983 | 131,423,068 | 1,319,437 | 130,028,544 | 75,087 | 9.5% |
| Combined | 1,968 | 273,560,720 | 3,973,782 | 269,393,920 | 193,018 | 19.8% |

Using the current public GPT-5.6 Sol rate card, the combined wait/status subset corresponds to an estimated 3,948 credits. This is a rate-card estimate from local telemetry, not a claim about the private subscription billing ledger.

Representative long-running trace

One child task ran for about 10 hours 55 minutes and contained:

  • 1,417 genuine model turns
  • 254 wait_agent calls
  • 31 list_agents calls
  • 316 write_stdin calls
  • 12 generic wait calls
  • 613 combined wait/status-only turns
  • about 93.17 million raw local tokens attributable to those wait/status turns

The wait/status subset was about 45% of that child task's genuine raw token volume. Most agent waits requested only 30 or 60 seconds before another model-mediated poll.

Steps to reproduce

  1. Start an Ultra task that delegates a genuinely long-running child task.
  2. Let the child run for at least 30-60 minutes while the parent or another child coordinates it.
  3. Observe repeated wait_agent, list_agents, write_stdin, or generic wait calls during otherwise quiet intervals.
  4. Inspect per-turn local token deltas, excluding copied child-history prefixes and unchanged cumulative snapshots.
  5. Note that status-only polling repeatedly creates distinct model turns and reprocesses a large cached context.

Expected behavior

Waiting for an agent or terminal process should be event-driven, or handled by the harness without repeatedly invoking the model. The model should wake when:

  • the child or process produces meaningful new output;
  • the child completes or requires attention;
  • the user sends new input; or
  • a deliberately bounded, low-frequency checkpoint is required.

Actual behavior

Quiet wait intervals repeatedly invoke the model so it can issue another polling tool call. Large cached prefixes are charged through the public credit model even though the turns produce almost no substantive user-facing work.

Why this is not ordinary Ultra fan-out

Ultra and explicit multi-agent delegation were intentional, and their extra cost is expected. The anomalous subset here is narrower: nearly one-fifth of corrected raw token volume came from turns whose only action was waiting or checking status.

The same corrected window attributed 68.1% of raw volume to Ultra and 58.7% to subagents. Those figures describe the chosen workload. This issue concerns the avoidable model-mediated polling inside that workload.

Evidence boundary

  • These figures come from local rollout telemetry, not OpenAI's internal billing ledger.
  • Raw local token volume is not the same as subscription usage.
  • The credit figure is an estimate using the published model rate card.
  • Copied historical token records were explicitly excluded and are not claimed as billing events.
  • I found no evidence here of a silent quota reduction.
  • No private prompts, task identifiers, repository names, local paths, account details, or transcript content are included.

Related reports

  • #34468 — parent chat polls child agents every minute
  • #33276 — wait-for-agents loop drained quota
  • #35108 — nested wait-agent polling
  • #13733 — terminal polling
  • #18394 — default wait timeout
  • #34361 — replay attribution work

Suggested fix direction

  • Make agent and terminal waits event-driven inside the harness.
  • Avoid a fresh model call when no meaningful state has changed.
  • Coalesce repeated status checks and apply backoff for long-running work.
  • Surface wait/polling usage separately in diagnostics.
  • Add regression coverage asserting that unchanged wait state does not create repeated billable model turns.

View original on GitHub ↗

19 Comments

github-actions[bot] contributor · 1 month ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #35108
  • #34468

Powered by Codex Action

dimasyankauskas · 1 month ago

Addendum: ten-day baseline comparison (July 15–24, 2026)

I extended the replay-corrected audit to compare the July 23–24 incident period with the preceding eight-calendar-day baseline. Copied child-history prefixes and unchanged cumulative token snapshots remain excluded.

| Metric | Jul 15–22 baseline | Jul 23–24 incident | Change |
|---|---:|---:|---:|
| Raw local model tokens/hour | 27.02M | 69.68M | 2.58× |
| Genuine model turns/hour | 214.43 | 546.80 | 2.55× |
| Raw tokens/genuine turn | 125.99K | 127.43K | +1.14% |
| Wait/status-only raw-token share | 9.54% | 17.80% | +8.26 percentage points |
| Subagent raw-token share | 28.46% | 51.58% | +23.12 percentage points |

The spike is therefore primarily a much higher frequency of genuine full-context model turns, not materially larger individual turns. The increased wait/status share strengthens the narrower harness concern in this issue: quiet coordination intervals are repeatedly re-entering the model.

I also compared two consecutive weekly-meter cycles. The current cycle produced 25.50M replay-safe raw local tokens per observed meter percentage point versus 26.28M in the previous cycle, a difference of about 3%. This does not support a newly smaller Pro allowance or changed meter denominator. It is consistent with the meter depleting faster because substantially more model turns were executed per hour.

There was substantial update activity during the window: six local Desktop install-start events were observed between July 21 and July 23, and the public openai/codex repository published 28 releases from July 15 through July 24 UTC. However, the activity spike began before the transition to Desktop build 5828, so build 5828 cannot be identified as the sole cause. A public ChatGPT/Codex service incident also overlapped part of July 23. These timings warrant investigation but do not establish causation.

Evidence boundary: these remain privacy-safe aggregates from local rollout telemetry, not OpenAI billing records. Ultra and multi-agent usage were intentional; the suspected defect remains repeated model-mediated polling during unchanged wait/status intervals.

bbingz · 1 month ago

Analysis (community)

Residual analysis for #35259 (Desktop re-enters the model during wait/status polling and burns credits) under docs/contributing.md — invitation-only. No unsolicited PR.

Root-cause hypothesis

wait_agent is a local timeout over an activity watch channel; on timeout the turn layer sets needs_follow_up and issues a fresh sampling request that re-ingests the full prefix, so long delegated work becomes N ≈ ceil(W/t) billable model turns. This is client-side re-entry, not a silent server quota cut.

@dimasyankauskas — this is an exceptional piece of forensic work; the 10-day baseline comparison (2.58x tokens/hr, +8.26pp wait/status share, +23.12pp subagent share) and the meter-cycle parity check are exactly the kind of evidence that isolates "more turns per hour" from "smaller allowance." Your "no evidence of a silent quota reduction" conclusion is, I think, the right read.

I want to add the missing piece you and the cluster (#32640, #35108, #34468, #32309) describe behaviorally but don't trace in code: the unified OSS root cause. At HEAD 4c43465133, wait_agent (codex-rs/core/src/tools/handlers/multi_agents_v2/wait.rs:178-196) blocks on a purely local tokio::time::timeout_at over a watch::Receiver sourced from InputQueue::subscribe_activity (input_queue.rs:49-54) — no server round-trip, no long-poll. On timeout it returns WaitAgentResult{timed_out:true} as a FunctionCallOutput. The turn layer then unconditionally sets needs_follow_up=true (turn.rs:2199, plus end_turn=false on any tool call at turn.rs:2369-2370), continues the loop, and issues a fresh run_sampling_request that re-ingests the full prefix via sess.clone_history().for_prompt(...) (turn.rs:295-308, history.rs:143-146). prompt_cache_key/previous_response_id are billing/transport discounts only — they do not skip the model turn. So N = ceil(W/t) polling inferences, each billing the prefix. That is your 2.58x.

Two corrections to the cluster, for the record: (1) the OSS wait_agent default is 30s, max 1h (config/mod.rs:209-211), not 50s — #32640's ~50s is the model's chosen timeout_ms on the code_mode wait tool path, a different handler; (2) out-of-range timeout_ms is rejected, not clamped (wait.rs:54-63). Neither changes the mechanism.

Ranked fix outline (all OSS): (a) re-arm wait_for_activity on TimedOut while the child is still running, instead of returning to the model (wait.rs:96-117) — collapses N polling turns into ≤1; (d) coalesce unchanged-status polls with backpressure; (b) raise/document default_wait_timeout_ms (defense-in-depth only; doesn't cover code_mode wait or explicit short choices); (c) suppress prefix re-billing for pure status polls (partially server-side).

Fail-first tests I'd want before any merge: a 10-min delegated child produces ≤1 model turn for the whole wait (today: ~20); a pure-status timeout does not issue a new run_sampling_request (today: does); steer/mailbox wake still fires immediately (regression guard for #18968/#28341). Happy to draft the test patch if a maintainer wants it.

Happy to refine measurements or a fail-first test patch if maintainers want one. No unsolicited PR from me.

dimasyankauskas · 1 month ago

@bbingz Thank you—this source trace is highly relevant and broadly consistent with the telemetry.

Two evidence boundaries are worth preserving:

  1. The 2.58× figure is the total tokens/hour increase, not an attribution entirely to wait polling. From the aggregate rates, additional wait/status volume was approximately 9.83M raw tokens/hour out of a 42.66M/hour total increase—about 23%. So this mechanism plausibly explains a material, directly observed part of the spike, rather than the whole increase.
  1. clone_history().for_prompt() establishes the logical model-visible context, while the WebSocket path may transmit an incremental delta using previous_response_id. OpenAI's API documentation says previous chain input remains billed as input tokens, but my ChatGPT Pro credit ledger is not directly observable. I am therefore keeping the conclusion at: repeated sampling requests are consistent with faster credit depletion, rather than proof of private subscription-billing semantics.

The fail-first tests are excellent. I would also add equivalent coverage for write_stdin / code-mode waiting, since terminal polling accounted for another 9.5% of raw volume. Given the repository's contribution policy, waiting for maintainer direction before drafting a patch makes sense.

dimasyankauskas · 1 month ago

Additional recurrence data: fixed 12-hour window

I ran the same replay-safe analysis on a separate fixed window (2026-07-25 05:08–17:08 UTC). The same model-mediated wait/status symptom persisted, but at lower intensity than the Jul 23–24 incident window.

Corrected aggregate totals:

  • 3,122 genuine model turns
  • 363,249,277 raw local tokens
  • 329 wait/status-only turns
  • 46,347,005 raw tokens on those turns (12.76% of all raw volume)
  • Agent coordination only (wait_agent / list_agents): 202 turns, 28,298,127 raw tokens
  • Terminal polling only (write_stdin / generic wait): 127 turns, 18,048,878 raw tokens

Repeated-polling evidence:

  • 62 consecutive wait/status streaks of two or more model turns
  • 203 wait/status turns inside those streaks
  • 29,261,446 raw tokens inside those streaks (63.1% of wait/status volume)
  • Longest uninterrupted streak: 13 model turns; median streak: 3 turns
  • Requested cadence still included 93 wait_agent calls at 30 seconds, 9 at 60 seconds, and 33 generic waits at 30 seconds

Comparison using the same definitions:

| Window | Raw tokens/hour | Turns/hour | Wait/status raw share | Subagent raw share |
|---|---:|---:|---:|---:|
| Jul 15–22 baseline | 27.02M | 214.43 | 9.54% | 28.46% |
| Jul 23–24 incident | 69.68M | 546.80 | 17.80% | 51.58% |
| New 12-hour window | 30.27M | 260.17 | 12.76% | 34.59% |

This places the new window 3.22 percentage points above the earlier wait/status baseline, but 5.04 points below the incident. It supports recurrence of the same mechanism without suggesting that the current intensity equals the incident peak.

Method and privacy boundary:

  • Started each rollout at its genuine live task_started boundary
  • Excluded 8,057 copied-prefix token records and 104 unchanged token snapshots
  • Counted only positive per-turn token deltas
  • Observed zero malformed records and zero cumulative resets
  • Aggregate-only: no prompts, task or thread identifiers, repository names, working directories, local paths, account details, transcript content, or local files are included
  • Raw local token volume is diagnostic telemetry, not the private subscription billing ledger
gaikman2015 · 28 days ago

I encountered the same issue on Codex TUI / CLI 0.145.0 on Windows with gpt-5.6-sol, Ultra, and multi-agent v2. A
parent turn ran for 16 hours 29 minutes without further user input or completion, recorded 255 wait_agent calls and 11
context compactions, and ended with another unmatched wait_agent call. The final five-minute polling cycles each re-
entered the model and processed about 213,000 tokens, overwhelmingly cached input.

phyrexia · 27 days ago

Independent corroboration from a different account, plus one methodological note that may help others reproduce your numbers.

I only report what I could verify. Where my own accounting is still unreliable I say so rather than post a figure.

1. The wait/status polling, in an 11-day session

Same mechanism you describe, on ChatGPT Pro with gpt-5.6-sol. One session stayed open for 11 days and produced a single 475 MB rollout.

I counted the blocking wait calls directly from their arguments:

  • 6,932 wait calls
  • 1,644 of them (23.7%) returned with the payload field empty — the wait elapsed and the delegated work had produced nothing
  • 35.7 hours of declared wait time in total
  • most common single form: a 30-second wait, used 3,127 times

The empty returns escalate over the life of the session:

day 1   (Jul 19)    124 waits,     0 empty
day 5   (Jul 23)    277 waits,    23 empty
day 8   (Jul 26)    815 waits,   136 empty
day 11  (Jul 29)    768 waits,   310 empty
day 12  (Jul 30)  1,748 waits,   523 empty

The session recorded 8 errors in 11 days. Nothing failed and nothing retried, which is why it ran that long unnoticed. On the last day my client logged 379 usage_limit_exceeded errors.

This supports @bbingz's root-cause reading: the empty return is the local timeout firing, and each one costs a fresh full-prefix turn.

2. A clean account exhausted a full weekly allowance in 407 model turns

After exhaustion I moved to a brand-new account on a different tier — no history, no prior usage. It was active for four hours (13:12–17:15 UTC) and reached 100% of its weekly cap.

Counted with your criteria (anchor on turn_context, exclude inherited prefixes, ignore unchanged cumulative snapshots, count genuine deltas only):

  • 407 genuine model turns
  • 1,840,805,399 genuine tokens

Four hundred turns for a week's allowance. This is the part I find hardest to explain as normal accounting, because there is no accumulated history to blame.

3. Methodological note: ~90 telemetry events per genuine turn

This is why I think several reports in these threads produce figures that cannot be reconciled with each other.

Counting token_count events overstates model turns by roughly two orders of magnitude. On one day I measured:

2,621,113  token_count snapshots
   29,206  turn_context events
     89.7  snapshots per genuine model turn

turn_context is the anchor that gives turn counts matching plausible activity. I had this wrong myself for several passes, and every derived figure was inflated until I fixed it. Your parser already handles this; it may be worth stating the anchor explicitly, since it is the single correction that reconciles the numbers.

What I am not reporting

My per-window token totals for the older account are still wrong — my delta accounting mishandles the gap left when an inherited prefix is skipped, which inflates the sum. I would rather leave that out than add a number to this thread that has to be withdrawn later. The wait counts and turn counts above do not depend on that calculation.

phyrexia · 27 days ago

Follow-up to my comment above: the empty-return rate correlates with the CLI version, and it looks like something between 0.144.6 and 0.146.0 fixed it.

I grouped every rollout in July by the version recorded in its session_meta, then counted blocking wait calls and how many returned with the payload field empty. These are counts of calls and of return values — they do not depend on any token accounting.

| CLI version | wait calls | empty returns | % empty | days seen (July) |
|---|---:|---:|---:|---|
| 0.144.6 | 10,828 | 1,824 | 16.8% | 19–27 |
| 0.143.0 | 653 | 62 | 9.5% | 09–11 |
| 0.144.5 | 2,672 | 201 | 7.5% | 16–19 |
| 0.145.0 | 7,390 | 460 | 6.2% | 22–30 |
| 0.144.1 | 1,567 | 55 | 3.5% | 11–13 |
| 0.144.3 | 2,980 | 69 | 2.3% | 13–19 |
| 0.142.4 | 116 | 2 | 1.7% | 03–09 |
| 0.146.0 | 617 | 4 | 0.6% | 30–31 |

Two things stand out.

0.144.6 is roughly 3x worse than any other version I ran, and it is the version that was active during the 11-day session I described above — it covered nine of those eleven days.

0.146.0 is effectively clean at 0.6%. That also resolved something that had confused me: when I measured the wait calls made by a separate, brand-new account, I got 0.6% empty and read it as "no busy-waiting happened there". It was not a different behaviour — that account simply ran on 0.146.0.

If this holds on other accounts, there is a change between 0.144.6 and 0.146.0 that addresses it, which gives a concrete range to bisect rather than a symptom description.

Caveats, stated plainly: this is one account's data; the samples are uneven (0.146.0 has only 617 wait calls against 10,828 for 0.144.6); and version attribution comes from each rollout's own session_meta, so a session spanning an upgrade is attributed to the version it opened with. I would not read the ordering of the middle rows as meaningful — the gap between 0.144.6 and 0.146.0 is what I would test.

Worth someone with 0.146.0 and heavy multi-agent use confirming whether the empty returns really did stop, or whether they are simply rarer in their workload.

phyrexia · 27 days ago

Follow-up on the version correlation I posted above: I think the fix is #34852, "Wake sleeping threads for queued agent mail", merged 2026-07-23.

It touches codex-rs/core/src/tasks/mod.rs (+16/-4). From its own description:

An idle thread with an outstanding durable sleep must resume when agent work arrives, even when that message would normally remain queued without triggering a turn. - Treat any pending mailbox message as wake-up work while a durable sleep is attached to the thread. - Keep requiring trigger_turn for idle threads that are not durably asleep.

That matches the empty returns exactly. Before it, the sub-agent's message landed in the mailbox but did not wake the sleeping supervisor. The supervisor stayed asleep until its own timeout fired, woke on its own, and found the message already sitting there — and that timeout wake is the billed turn that re-ingests the full prefix. The work was already done; what was missing was the notification.

This lines up with the dates in my table. The fix merged Jul 23. I was still running 0.144.6 from Jul 19 to Jul 27 at 16.8% empty returns, i.e. several days after the fix existed. 0.146.0, which includes it, gives 0.6%.

It also matches @bbingz's reading of the source further up this thread.

Two adjacent commits in the same range suggest this was a recognised problem rather than an incidental change: #34887 "Allow disabling the multi-agent wait tool" and #34969 "Keep the sleep tool outside code mode".

Caveat: I identified this by diffing the two tags and matching commit messages against the observed behaviour — I have not built both versions to confirm the causal link. Someone on the team can verify that far faster than I can.

jkammerland · 25 days ago

I still experience heavy preference for polling in 1.46.0. Made a full post here:
https://github.com/openai/codex/issues/32309#issuecomment-5160186183

seattled23 · 25 days ago

I built and tested a V2-only root-cause patch for the model re-entry mechanism traced in this thread.

Current main (5157493c23, 2026-08-02) still wraps the parent InputQueueActivity watch receiver in timeout_at. When that local deadline expires, wait_agent returns a function result and the turn loop samples the model again even though agent state has not changed. #34852 improves delivery of queued agent mail, but it does not remove this residual timer-driven re-entry path.

Patch: https://github.com/seattled23/codex/commit/74b2624e2d33edb07e3c2ad0d56f191f725b8c2d

The patch:

  • removes timeout_ms and the deadline from MultiAgentV2 only;
  • awaits the existing watch receiver until mailbox activity or user steering;
  • preserves the V1 targeted-wait timeout contract;
  • preserves the V2 timed_out result field for wire compatibility (always false);
  • preserves the newer wait_agent_enabled configuration gate on current main.

Validation against current main: 11 matched tests passed, including remaining pending without activity, waking on queued/new mailbox activity, preserving the enable/disable gate, the event-only tool schema, and the end-to-end steering interruption test. I also ran the repository formatter and clippy gate on the release-tag version of the same patch.

This deliberately does not claim to solve missing child-death notifications: if a terminal path fails to enqueue mailbox activity, that lifecycle defect still needs its own fix. It does eliminate periodic inference turns while the mailbox is genuinely quiet.

I have not opened a PR because the contribution guide says code changes are invitation-only. Happy to rebase or submit one if a maintainer wants this direction.

is-real-uuu · 24 days ago

中文:Windows Codex Desktop 独立排查结果

我在 Windows 11 的 Codex Desktop 上遇到了相同问题。以下结论是我针对自己机器上的 Codex rollout 日志独立排查、逐条统计后得到的,不是根据本 Issue 的数据推测,也不是直接复述现有报告。排查完成后,我才发现这里已有相同机制的反馈。

环境

  • Codex Desktop:26.727.51351
  • 模型:GPT-5.6 Sol
  • 推理强度:Low
  • Service tier:Default(未使用 Fast/优先模式)
  • 场景:长上下文会话中执行 Maven 编译/测试等长时间终端命令

本地日志统计结果

按本地日期统计,一个工作日的 rollout telemetry 为:

  • 总 Token:181,420,169
  • 输入 Token:181,149,482
  • 缓存输入 Token:177,126,144(占输入的 97.78%)
  • 非缓存输入 Token:4,023,338
  • 输出 Token:207,769
  • 推理 Token:39,202(仅占总量约 0.022%)

其中一个主要会话产生了 145,083,309 Token,约占当天总量的 80%。

我继续把该会话中的工具调用和紧随其后的 token delta 关联统计,发现:

  • generic wait 调用共 702 次
  • 其中 yield_time_ms=1000:552 次
  • yield_time_ms=10000:139 次
  • yield_time_ms=30000:11 次
  • 仅这些 wait-result 回合就产生 109,320,725 Token
  • 占主要会话 Token 的 75.35%
  • 占当天全部 Token 的 60.26%
  • 平均每次 wait 约重新处理 155,700 Token,绝大部分是缓存上下文

这说明异常消耗并不是 High/xHigh reasoning 或 Fast 模式造成的。该会话明确使用 Low reasoning 和 Default service tier,而 reasoning token 只有 39,202。主要原因是:后台命令未结束时,每次短间隔 wait 都重新进入模型,并重新携带当时约 15 万以上 Token 的长上下文。即使终端没有产生有意义的新输出,也会形成新的完整模型回合。

期望行为

  1. 终端进程等待应由客户端/harness 事件驱动处理,状态未变化时不应重新调用模型。
  2. 对连续无输出的等待自动采用指数退避并合并状态检查。
  3. 禁止或限制 1 秒级模型介入轮询,并提供可配置的最小等待间隔和最大连续 wait 次数。
  4. 进程结束、有新输出、需要审批或用户输入时再唤醒模型。
  5. 在诊断界面单独显示 wait/status-only 回合及其 Token,便于定位异常消耗。

说明:以上均为本机 rollout telemetry 的脱敏汇总,不代表 OpenAI 内部计费账本,也不包含任务 ID、文件路径、仓库名称、账号信息、业务内容或原始日志。

---

English: Independently diagnosed on Windows Codex Desktop

I reproduced the same behavior on Codex Desktop for Windows 11. The findings below were independently derived by analyzing and correlating my own local Codex rollout logs. They were not inferred from or copied from this issue. I found this existing report only after completing the investigation.

Environment

  • Codex Desktop: 26.727.51351
  • Model: GPT-5.6 Sol
  • Reasoning effort: Low
  • Service tier: Default (not Fast/priority)
  • Workload: long-context session running long Maven build/test commands

Sanitized local telemetry

For one local calendar day:

  • Total tokens: 181,420,169
  • Input tokens: 181,149,482
  • Cached input tokens: 177,126,144 (97.78% of input)
  • Uncached input tokens: 4,023,338
  • Output tokens: 207,769
  • Reasoning tokens: 39,202 (about 0.022% of total)

One main session contributed 145,083,309 tokens, about 80% of the daily total.

I then correlated tool calls with the immediately following per-turn token deltas:

  • 702 generic wait calls
  • 552 used yield_time_ms=1000
  • 139 used yield_time_ms=10000
  • 11 used yield_time_ms=30000
  • Wait-result turns alone accounted for 109,320,725 tokens
  • 75.35% of the main session
  • 60.26% of the entire day's raw local token volume
  • Approximately 155,700 tokens were reprocessed per wait on average, mostly cached context

This rules out High/xHigh reasoning or Fast mode as the primary cause in this case. The session was consistently configured for Low reasoning and Default service tier, and reasoning usage was only 39,202 tokens. The dominant mechanism was repeated model re-entry while waiting for a terminal process: every short wait turn carried a context of more than roughly 150k tokens even when the process produced no meaningful new output.

Expected behavior

  1. Terminal-process waiting should be event-driven in the client/harness and should not invoke the model when state is unchanged.
  2. Repeated no-output waits should be coalesced and use automatic exponential backoff.
  3. One-second model-mediated polling should be prevented or bounded, with configurable minimum wait intervals and maximum consecutive waits.
  4. Wake the model only when the process exits, meaningful output arrives, approval is required, or the user sends input.
  5. Diagnostics should expose wait/status-only turns and their token usage separately.

These figures are sanitized summaries from local rollout telemetry, not claims about OpenAI's internal billing ledger. No task IDs, local paths, repository names, account details, business content, or raw logs are included.

AiurArtanis · 15 days ago

Windows Pro 20x addendum: wait polling and rate-bucket mismatch

Environment:

  • Codex App (Microsoft Store): 26.803.10989.0
  • Bundled Codex CLI: 0.147.0-alpha.6.6
  • Windows: Microsoft Windows NT 10.0.19045.0 x64
  • Subscription: ChatGPT Pro (20x)

Observed in a long-running Reborn-related task (sanitized local rollout aggregates; no prompts, paths, account identifiers, or checkout data included):

  • The final 76-minute parent turn issued 27 model-mediated tool calls: 26 wait calls and 1 list_agents call.
  • Of the wait calls, 16 requested a 10-second wait, 9 requested 1 second, and 1 requested 30 seconds.
  • The same turn recorded 254 token snapshots. Its cumulative local usage increased by approximately 44,201,251 input tokens (42,657,024 cached; 1,544,227 uncached), 110,961 output tokens, and 45,649 reasoning tokens.
  • The parent and both review children were configured as gpt-5.6-sol with reasoning_effort=max; there is no evidence that these children were configured as a fast model.
  • The rate-limit snapshot for the same parent turn reported plan_type=pro but limit_id=codex_bengalfox, limit_name=GPT-5.3-Codex-Spark, and primary.used_percent=0, despite the turn context identifying Sol/max. This makes model/usage attribution ambiguous.
  • Separately, the Codex Reborn local watcher for loopback port 9338 wrote 23,579 renderer-timeout lines over about 20.9 hours after the renderer target was absent. It was local CDP polling only; no 9338 listener remains, and the watcher process command line did not show an OpenAI model/API client. I therefore cannot claim that Reborn itself billed model usage.

Expected behavior:

  • A wait for unchanged agent/process state should be handled event-driven or with bounded backoff, without repeatedly re-entering the model and reprocessing a large cached context.
  • Usage telemetry should unambiguously identify the billed model/bucket and distinguish compaction, wait polling, and user turns.
  • A terminated renderer should stop all Reborn watchers promptly.

Requested investigation:

  1. Confirm whether these repeated wait/status turns are charged to the Pro weekly allowance.
  2. Explain why a Sol/max turn reports the Spark rate-limit bucket.
  3. Add a regression preventing repeated model sampling during unchanged waits, and expose a causal usage record for background/coordination work.
  4. Ensure renderer-loss cleanup terminates all desktop/plugin watchers.

This is a sanitized addendum to the existing wait/status polling report; raw rollout files can be provided privately if maintainers need them.

Mateusz-Bugaj · 12 days ago

Environment:
Windows
Codex Desktop
Prolite
weekly window: 2026-08-13 08:13:35 → 2026-08-20 08:13:35

Observed:
6,829 genuine local model turns
554 redundant wait_agent re-entries

Waste:
73,709,145 raw input tokens
73,169,792 cached input
539,353 uncached input
19,553 output

7,973,650 input-equivalent tokens
995.314 estimated credits

Impact:
7.50% of locally observed usage
~6.00 percentage points of the entire weekly rate-limit window

Models:
551 / 554 redundant wakeups were GPT-5.6 Sol

AmeerJ97 · 6 days ago

Independent Linux reproduction on Codex CLI 0.149.0 with a ChatGPT Pro 20x
account. I analyzed the complete local rollout corpus from a weekly reset at 0%
used through the first observed 33% state.

Methodology:

  • Read every local rollout file end-to-end: 2,242 files / 4.60 GB.
  • Selected 71 files with events inside the fixed reset-to-cutoff window.
  • Excluded copied child history before each child's live session start.
  • Counted each changed cumulative token snapshot once.
  • Excluded 77 unchanged/copied snapshots from 7,074 in-window snapshots.
  • Attributed each genuine turn using its own last_token_usage, actual model,

and reported quota-bucket signature.

  • Did not include prompts, repository names, local paths, account identifiers,

or credentials.

Corrected ordinary-pool window:

  • 6,387 genuine model turns.
  • 1.187B input tokens.
  • 1.167B cached input tokens.
  • Approximately 13,091 credits using the public Sol/Terra rate card.
  • Visible weekly meter: 0% → 33%.

Wait/status-only subset:

| Class | Turns | Input | Cached input | Estimated credits |
| --- | ---: | ---: | ---: | ---: |
| Terminal/process wait only | 570 | 128.5M | 127.7M | 1,334 |
| Shell sleep + status poll only | 263 | 59.2M | 58.7M | 598 |
| Agent wait/list only | 106 | 40.0M | 39.5M | 347 |
| Total | 939 | 227.7M | 225.9M | 2,278 |

The wait/status subset is 17.4% of reconstructed ordinary credits, equivalent
to roughly 5.7 percentage points of the inferred weekly allowance. The largest
single root had 450 wait/status-only turns and about 947 estimated credits in
that subset.

Repeated patterns include identical write_stdin calls, 25–50 second shell
sleeps followed by unchanged CI/pipeline status checks, and repeated
wait_agent/list_agents calls. Each interval re-entered the model and
reprocessed a large cached prefix.

This supports the requested fix direction in this issue: waits should be
event-driven in the harness, unchanged state should not create a new model
turn, and wait/polling usage should be exposed separately.

I can provide maintainers with sanitized per-turn and per-session metadata
privately if requested. These are local rate-card estimates, not claims about
OpenAI's private server billing ledger. No chat content is included in this
report.

nos1609 · 6 days ago

Fresh, narrow follow-up for the direct V2 wait path on Codex CLI 0.149.0. This sample does not use the nested V1 functions.exec wrapper described in #35108.

Environment:

  • Rocky Linux 10.2, x86_64
  • VS Code Web with the OpenAI extension 26.818.32112
  • Codex CLI 0.149.0
  • Fresh root task using direct V2 multi-agent tools

During a fixed 24-hour window, the root made 91 direct wait_agent calls:

  • 50 returned because the requested timeout expired: 55%;
  • 41 returned after agent completion;
  • every call waited for one target;
  • explicit timeouts included 30, 60, 120, and 180 seconds;
  • observed wait duration: median 30.16 s, p95 120.07 s, maximum 180.07 s.

The 50 timeout-only returns caused 50 model re-entries with:

  • 6.263M total processed tokens;
  • 6.252M input tokens;
  • 6.106M cached input tokens.

Completed waits caused another 5.001M processed tokens, but this comment isolates the timeout-only subset because those 50 re-entries occurred without a terminal agent event.

The analysis used each changed cumulative token snapshot once and excluded copied child-history prefixes. The sample therefore does not count fork replay as fresh model activity.

This confirms that the timer-driven re-entry mechanism remains present in 0.149.0. Increasing the timeout reduces poll frequency, but it does not fix the lifecycle: every expired wait still returns unchanged state to the model and reprocesses a large cached context.

The runtime should keep the parent suspended and re-arm waits internally. It should wake the model only for agent completion, failure, actionable output, user input, or an explicitly requested checkpoint.

No prompts, task identifiers, repository names, local paths, account data, or credentials are included.

nos1609 · 2 days ago

Fresh sanitized reproduction of model-mediated wait re-entry on the current 0.149.x line.

During one parent turn, the parent delegated work to an existing task and then issued at least 10 consecutive wait_threads calls with timeoutMs=60000. Each timeout returned control to the model, which immediately submitted another 60-second wait. There was no intervening user message and no terminal task event that justified substantive parent work. The sequence consumed roughly 10 minutes, after which the parent also performed manual rollout-status polling.

This is a task-level wait API occurrence rather than the nested V1 wrapper path. I am not attaching a token estimate for this sample; the narrow evidence is the repeated timeout -> model re-entry -> identical wait cycle within one parent turn.

Project names, prompts, local paths, hostnames, account data, and task IDs are omitted.

nos1609 · 1 day ago

Fresh narrow reproduction on the current 0.149.x line using Codex Desktop and a local orchestrator waiting on an existing Remote SSH task.

The target's observable task state did not change: repeated wait_threads(timeoutMs=0) snapshots kept the same revision, reported the aggregate thread as active, and reported the latest terminal turn as completed with a final response.

Despite that unchanged state, within 5 minutes 11 seconds the parent issued five completed wait_threads(timeoutMs=60000) calls and started a sixth. Over the same interval, six model token snapshots increased the cumulative local total_tokens counter by 725,168.

Per re-entry:

  • input tokens ranged from 140,449 to 147,821;
  • cached input ranged from 138,880 to 146,048;
  • output remained small.

There was no intervening user input and no target revision or terminal event. The parent was therefore repeatedly sampled to wait on a target that had no new work to report. The aggregate active bit appears to have kept the polling loop alive even though the latest terminal turn was already completed.

This is local rollout telemetry, not a claim about private billing or subscription accounting. No prompts, task IDs, project or repository names, paths, hostnames, addresses, account data, or credentials are included.

FromAriel · 12 hours ago

Cross-linking this implementation-level consumption amplifier to #41220, the meta tracker for abnormal Codex usage/quota depletion and usage-accounting inconsistencies. Your corrected telemetry showing 19.8% of raw local token volume in wait/status-only turns is especially useful because it demonstrates one mechanism that can reduce effective usable subscription capacity even if the quota arithmetic itself is correct. The tracker explicitly distinguishes this class from entitlement/metering defects.