Skip to content

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

Open

Description

@dimasyankauskas

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

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.

Activity

  1. added
    bugSomething isn't working
    appIssues related to the Codex desktop app
    subagentIssues involving subagents or multi-agent features
    tool-callsIssues related to tool calling
    rate-limitsIssues related to rate limits, quotas, and token usage reporting
    on Jul 24, 2026
  2. dimasyankauskas commented on Jul 24, 2026

    @dimasyankauskas
    Author

    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.

  3. bbingz commented on Jul 25, 2026

    @bbingz

    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.

  4. dimasyankauskas commented on Jul 25, 2026

    @dimasyankauskas
    Author

    @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.

    2. 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.

  5. dimasyankauskas commented on Jul 25, 2026

    @dimasyankauskas
    Author

    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
  6. gaikman2015 commented on Jul 30, 2026

    @gaikman2015

    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.

  7. phyrexia commented on Jul 31, 2026

    @phyrexia

    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.

  8. phyrexia commented on Jul 31, 2026

    @phyrexia

    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.

  9. phyrexia commented on Aug 1, 2026

    @phyrexia

    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.

  10. jkammerland commented on Aug 2, 2026

    @jkammerland

    I still experience heavy preference for polling in 1.46.0. Made a full post here:
    #32309 (comment)

  11. 3 remaining items

  12. Mateusz-Bugaj commented on Aug 15, 2026

    @Mateusz-Bugaj

    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

  13. AmeerJ97 commented on Aug 21, 2026

    @AmeerJ97

    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.

  14. nos1609 commented on Aug 22, 2026

    @nos1609

    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.

  15. nos1609 commented on Aug 26, 2026

    @nos1609

    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.

  16. nos1609 commented on Aug 26, 2026

    @nos1609

    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.

  17. FromAriel commented on Aug 27, 2026

    @FromAriel

    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.

  18. Oreho-git commented on Aug 28, 2026

    @Oreho-git

    Addendum: Codex CLI 0.150.1 — 8m24.481s avoidable coordinator loop after an explicit no-duplicate instruction

    Environment:

    • macOS, Codex Desktop orchestration
    • codex-cli 0.150.1
    • GPT-5.6 Sol, Ultra
    • Multi-agent task coordinated through a local orchestration CLI

    This is a sanitized, narrow reproduction from one rollout. No prompts, repository names, task IDs, local paths, account identifiers, or credentials are included.

    Timeline

    All timestamps below are UTC from the rollout JSONL:

    • 16:54:34.327 — the user explicitly warned that the coordinator was duplicating a completed validation pass.
    • 16:54:51.698 — the coordinator sent a general instruction not to repeat the full suite, but left the delegated review scope broad instead of limiting it to the two recently changed boundaries.
    • 17:02:58.808 — only after further user intervention did the coordinator send the exact bounded instruction: check only the two changed boundaries and stop.
    • 17:03:23.196 — the worker returned worker_done, 24.388 seconds after the exact bounded instruction.

    The avoidable interval from the first explicit warning to the corrective bounded instruction was exactly 504.481 seconds (8m24.481s).

    Model-mediated activity in that interval

    The rollout contains 34 model-issued tool calls:

    • 7 orchestration blocking waits
    • 8 write_stdin wait continuations
    • 7 orchestration status checks
    • 5 terminal status reads
    • 1 worker-status read
    • 4 coordination sends
    • 1 worker release
    • 1 plan update

    Thus 28 of 34 calls were wait/status activity while the coordinator already had enough information to narrow the review.

    Cumulative local rollout telemetry from the last snapshot before the warning (16:54:34.268) to the first snapshot after the corrective instruction (17:02:59.577) increased by:

    • input: 7,072,499 tokens
    • cached input: 7,035,136 tokens
    • output: 7,796 tokens
    • reasoning output: 2,681 tokens
    • total: 7,080,295 tokens

    Cached input was 99.47% of input in this interval. These are local diagnostic counters, not a claim about the private subscription billing ledger.

    Why this is a useful variant of the existing issue

    This was not only a quiet child wait. It combined model-mediated polling with a scope-control failure: after the user said validation was being duplicated, the coordinator acknowledged the instruction but continued monitoring a broader delegated audit for more than eight minutes. When the same coordinator finally issued the exact two-boundary stop condition, the worker completed in 24.388 seconds.

    Expected behavior

    • Convert an explicit no-duplicate instruction into a bounded worker contract immediately.
    • Reuse already completed validation evidence.
    • Suspend event-driven until worker completion or actionable output instead of repeatedly re-entering the model for unchanged status.
    • If a worker expands beyond the bounded contract, interrupt it once rather than continuing status polling.
    • Expose wait/status-only turns and their local token deltas in diagnostics.
  19. tagorr commented on Sep 8, 2026

    @tagorr

    I can reproduce the same wait_agent polling behavior in Codex TUI/CLI, not Desktop, with a different parent/worker model setup.

    I also submitted /feedback from the affected session with logs and diagnostics attached.

    Uploaded parent thread ID: 01a079cd-a44b-7ef2-8afb-45a3c5659a79

    Environment

    • ChatGPT plan: Plus
    • Codex CLI: 0.153.1
    • OS: Windows 10 IoT Enterprise LTSC 2021, 21H2, build 19044.7663, x64
    • Parent: gpt-6-astra, reasoning low
    • Workers: 2 × gpt-5.6-luna, reasoning xhigh
    • No other Codex sessions or instances were running during this interval.

    Reproduction

    The parent delegated one bounded implementation task to a Luna worker and then waited for completion.

    During this single parent turn, Astra made 69 tool calls, including:

    • 47 × wait_agent
    • 9 × exec
    • 7 × send_message
    • 2 × spawn_agent
    • 2 × interrupt_agent

    All 47/47 wait_agent calls used:

    timeout_ms = 30000
    

    and all 47/47 returned only a timeout:

    Wait timed out.
    timed_out: true
    

    So the parent repeatedly re-entered the model approximately every 30 seconds while the workers were still active.

    47 × 30 seconds = 23m 30s of timeout waits during a parent task lasting about 33 minutes.

    Parent polling cost

    The 47 timeout-only wait_agent turns accounted for:

    input tokens:            7,130,181
    cached input tokens:     7,114,112
    output tokens:               3,168
    reasoning output tokens:     1,331
    

    Average per empty poll: ~151.7k input tokens.

    The full Astra parent turn used:

    input tokens:           10,463,897
    cached input tokens:    10,406,016
    output tokens:               8,948
    reasoning output tokens:     3,266
    

    So timeout-only polling represented about 68% of the parent turn's raw input-token volume.

    The session guidance itself included:

    When calling wait_agent, prefer longer waits (minutes) to avoid busy polling.

    but the parent still used 30-second waits repeatedly.

    Workers were actively doing work

    This was not a case of idle or dead workers.

    Worker 1 used about 14.05M input tokens and was interrupted twice. After the second interruption, the parent discovered that it had already produced a partial patch of:

    2 files changed
    127 insertions
    6 deletions
    

    A replacement Luna worker then continued the task and used about 5.46M input tokens before the overall account limit was reached.

    Combined, the two Luna workers used:

    input tokens:           19,514,162
    cached input tokens:    18,811,392
    output tokens:              71,914
    reasoning output tokens:    24,182
    

    The implementation did not reach its build/validation stage before exhaustion.

    Server-reported usage during the same interval

    Rate-limit telemetry in the parent rollout shows:

    21:09:17 UTC
    5h used:     53%
    weekly used:  9%
    
    21:42:23 UTC
    5h used:    100%
    weekly used: 16%
    

    So over 33m 06s, server-reported usage changed by:

    • 5h window: 53% → 100%
    • weekly window: 9% → 16%

    The 5h metric therefore increased by the full remaining 47 percentage points before reaching the 100% cap and terminating the run.

    There was no concurrent Codex activity on the account during this interval.

    Measured Luna Max baseline

    I also checked a real Luna Max workload from the same Plus account earlier the same day, rather than relying only on subjective previous experience.

    Over one continuous 127-minute interval, all 12/12 recorded turn contexts used gpt-5.6-luna at max reasoning.

    During that interval, Luna processed:

    input tokens:            9,538,330
    cached input tokens:     8,998,400
    output tokens:              86,771
    reasoning output tokens:    55,170
    

    Server-reported account usage over the same interval changed from:

    5h used:      2% → 10%  (+8 percentage points)
    weekly used: 89% → 90%  (+1 percentage point)
    

    So this was not an idle session: Luna Max processed about 9.54M input tokens of actual work while the 5h allowance increased by only 8 percentage points.

    What this implies for the two Luna workers

    The two Luna X-High workers in the affected Astra run processed 19.51M input tokens combined, or about 2.05× the raw input processed by the measured Luna Max control.

    Even using a deliberately generous worker-side estimate and scaling the measured Luna Max allowance consumption linearly by raw input volume:

    8 pp × (19.514M / 9.538M) ≈ 16.4 percentage points
    

    the two Luna workers would account for roughly 16.4 of the observed +47 percentage points.

    That leaves roughly 30.6 percentage points — about 65% of the observed 5h increase — unexplained by the Luna workers under this conservative scaling.

    The only remaining Codex workload in this orchestration tree was the Astra parent, which processed 10.46M input tokens, including 7.13M input tokens spent solely on the 47 timeout-only wait_agent polls.

    The rollout does not expose an exact per-model breakdown of the server-side allowance charge, so this calculation is not intended as an exact billing attribution. But the measured Luna baseline makes it difficult to explain most of the +47 percentage-point increase as normal Luna worker consumption alone.

    What the rollout establishes directly is that 7.13M parent input tokens were spent solely on 47 timeout-only polls that returned no new result, during a run in which the account's 5h allowance went from 53% to the 100% cap in about 33 minutes.

    Expected behavior

    A long-running subagent should not require the expensive parent model to re-enter inference every ~30 seconds just to discover that nothing has changed.

    Ideally wait_agent should block/event-wait until:

    • the worker completes,
    • the worker reports a blocker/error, or
    • a genuinely long timeout expires,

    without repeatedly reprocessing the full parent context.

  20. PracticalSwan commented on Sep 9, 2026

    @PracticalSwan

    Current-version Windows reproduction from a privacy-safe local rollout audit:

    • Codex Desktop AppX 26.903.8094.0
    • codex-cli 0.153.4
    • GPT-5.6 Sol
    • one long root turn coordinating three long-running local operations

    The turn issued 72 model-mediated write_stdin polls while those operations were still running: 18 for one download, 39 for the first training process, and 15 for the second training process. Associating each poll call with its immediately following per-response token_usage_record gives:

    • 10,808,770 input tokens
    • 10,740,992 cached input tokens
    • 67,778 uncached input tokens
    • 7,508 output tokens
    • 10,816,278 raw local tokens total

    The same root turn's final cumulative local usage record was 19,580,416 tokens, so these polling responses account for about 55.2% of that turn's raw local token volume. Most polls requested a 30-second write_stdin wait and re-entered with roughly 132k–148k input tokens, overwhelmingly cached.

    Immediately before the terminal error, the rollout's rate-limit snapshot showed the 300-minute primary window at used_percent=100.0; the turn then ended with usage_limit_exceeded. This is local rollout telemetry, not a claim about the private billing ledger or an exact attribution of subscription usage to tokens. It does show the same unnecessary full-context re-entry pattern still reproduces on 0.153.4/Windows.

    No prompt text, project/repository name, thread ID, local path, account identifier, or credential is included here.

  21. MatiasSF9 commented on Sep 13, 2026

    @MatiasSF9

    I have another concrete reproduction of this behavior, this time with GPT-6 Astra, including local rollout token telemetry and Codex rate-limit telemetry.

    This was a real repository/process-engineering task using Astra High with a few independent reviewer subagents. The root Astra thread contained only 3 user turns, but produced 105 model sampling requests.

    Root-thread totals:

    • 105 model sampling requests
    • 16,761,653 input tokens
    • 16,357,632 cached input tokens
    • 53,465 output tokens
    • 9,440 reasoning-output tokens
    • 97.6% of input was cached
    • median input per sampling request was approximately 179k tokens

    The coordination overhead was particularly notable:

    • 17 wait_agent calls
    • those wait_agent cycles alone corresponded to approximately 3.47M input tokens
    • that is approximately 20.7% of all root-thread input
    • including list_agents / related status coordination brings the share to roughly 24%

    During the run, the parent context grew beyond ~200k input tokens per sampling request. As a result, each otherwise-small coordination decision could re-enter Astra against a very large cached prefix.

    The Codex rate-limit telemetry from the same rollout also shows nearly one complete 5-hour usage window being consumed, followed by a reset and nearly another complete 5-hour window during the same task.

    I am not claiming that raw local tokens map 1:1 to subscription quota. The interesting observation is the mechanism: a substantial fraction of the model-visible work was spent re-entering the parent model to coordinate or wait for child agents rather than performing new semantic work.

    This seems consistent with this issue's main finding, but demonstrates it on GPT-6 Astra and on a much smaller, ordinary engineering workflow rather than a 10+ hour stress case.

    The current Codex source already tells the model to "prefer longer waits (minutes) to avoid busy polling." That helps with cadence, but it does not eliminate the underlying amplification: a timeout/no-change result can still cause another model sampling request over the accumulated parent context.

    The stronger fix still appears to be architectural:

    • waiting for child agents/processes should be runtime/event-driven;
    • a no-state-change timeout should not require model inference;
    • the parent model should wake only when a child completes, produces meaningful new state, requires attention, or a deliberately bounded checkpoint is reached.

    Related Astra quota report: #42987.

    I can provide a redacted per-sampling/tool breakdown from the rollout if useful.

  22. mlk1278 commented on Sep 15, 2026

    @mlk1278

    This is a severe issue in the harness and is destroying my limits. Genuinely can't believe they haven't fixed this yet.

  23. tagorr commented on Sep 15, 2026

    @tagorr

    Yeah, I agree. This is a persistent and pretty severe harness issue, and it’s surprising that it has survived across so many Codex versions.

    For now, I found a temporary workaround that has been working in my testing:

    [features.multi_agent_v2]
    enabled = true
    min_wait_timeout_ms = 1500000
    default_wait_timeout_ms = 1500000
    max_wait_timeout_ms = 1500000

    This forces wait_agent to use a 25-minute timeout instead of falling into the 30-second polling loop. It still wakes early when the worker produces activity or completes, so it does not actually delay completion by 25 minutes.

    It’s only a mitigation, not a real fix, but in my telemetry it eliminated the repeated 30-second parent-model re-entry pattern.

  24. mlk1278 commented on Sep 16, 2026

    @mlk1278

    Yeah, I agree. This is a persistent and pretty severe harness issue, and it’s surprising that it has survived across so many Codex versions.

    For now, I found a temporary workaround that has been working in my testing:

    [features.multi_agent_v2]
    enabled = true
    min_wait_timeout_ms = 1500000
    default_wait_timeout_ms = 1500000
    max_wait_timeout_ms = 1500000
    This forces wait_agent to use a 25-minute timeout instead of falling into the 30-second polling loop. It still wakes early when the worker produces activity or completes, so it does not actually delay completion by 25 minutes.

    It’s only a mitigation, not a real fix, but in my telemetry it eliminated the repeated 30-second parent-model re-entry pattern.

    I have this applied as well. It fixed that issue but there's something else funky going on (or it didn't work). Astra is also just a seemingly insanely greedy model. I just tweeted at Tibo... My last run was a 332:1 input:output token ratio.

  25. cintaccs commented on Sep 17, 2026

    @cintaccs

    Additional macOS Desktop evidence and a request for supported guidance: explicit project instructions already prohibited model polling and delegated build/test waiting to a non-AI release supervisor, yet we found repeated model-driven sleep/status checks and a separate 20-minute AI heartbeat still active after the supervisor had written terminal failure receipts. Quiet-on-unchanged instructions suppress messages, but do not prevent model invocations.

    We are replacing the redundant heartbeat with a bounded local terminal-file observer and one codex queue --thread ... --message ... handoff (CLI 0.155.0-alpha.2.6). What is the supported Desktop mechanism for durable, event-driven task wakeup with no model calls while waiting, including restart recovery and delivery acknowledgement? Is there a recommended way to enforce this across parent/subagent tasks rather than relying only on instructions?

    Related visibility issue: child tasks can be read, renamed and moved with app tools, but did not appear in the sidebar section listing; completed same-directory forks did appear. Is there a supported persistent role/agent view that exposes the actual workers without creating additional active tasks? No private logs or account data included.

  26. renesugar commented on Oct 2, 2026

    @renesugar

    Additional reproducible subscription-quota evidence is available in this public waiting-inference analysis, with links to every individual run report and the inspected Codex source revision.

    The workload was one deterministic local sleeper lasting 3690 seconds per trial, with no substantive changes before completion. On Codex host and CLI 0.160.0:

    • Astra/low baseline: 26 estimated pre-completion sampling records; 15 pp five-hour and 2 pp weekly quota drain.
    • Astra/low mitigation: 1 estimated parent round; 3 pp five-hour and 1 pp weekly drain.
    • GPT-6.1 Sol/max baseline: the same 26 estimated sampling records; 3 pp five-hour and 1 pp weekly drain.

    The Codex mitigation did not downgrade the parent or use a cheaper monitor. It kept terminal re-arming inside deterministic JavaScript, avoiding a model return at each inner timeout. Separately, OMP 18.4.9 with openai-codex subscription authentication measured 2 pp/1 pp in baseline, versus 0 pp/0 pp at the reported resolution with an idle Astra/low parent and one Luna/low wait monitor (0 parent / 3 worker rounds).

    These observations connect unchanged waiting, actual pre-completion model activity, and subscriber quota consumption. No API-price conversion was used. They do not attribute every quota charge to a request: headroom/context differed, Codex included an hourly host-required message, and OMP baseline finalization was delayed about 15m22s. All included targets completed once and no reset crossed an individual trial. Zero reported movement is not zero token use.

    The report distinguishes the prompt-policy amplifier from terminal/Code Mode continuation boundaries and recommends completion-driven runtime waiting. Closely related terminal-path evidence belongs in #13733.

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

    appIssues related to the Codex desktop appbugSomething isn't workingrate-limitsIssues related to rate limits, quotas, and token usage reportingsubagentIssues involving subagents or multi-agent featurestool-callsIssues related to tool calling

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions