Sub-agent busy-waiting burns a week of quota: 6,932 blocking waits, 23.7% returning empty, in one 11-day session
Plan: Pro. Client: Codex CLI. Period: 2026-07-19 to 2026-07-30.
This is not a quota-accounting report. The accounting may well be correct — the
problem is what the client spends the quota on. In my case a single
long-lived session consumed 71% of my account's total token usage for the
period, and a large share of that was spent waiting for sub-agents rather than
doing work.
All figures below are counted from my own local rollout file.
Shape of the session
One session stayed open for 11 days and produced a single 475 MB rollout with
125,799 events. It was a multi-agent orchestration: 2,562 sub-agent activity
events, plus an agent-to-agent message tree.
The supervising agent polls its sub-agents with a blocking wait call. Those
calls look like this:
{"session_id":<id>,"chars":"","yield_time_ms":30000,"max_output_tokens":10000}
I counted 6,932 of these wait calls. 1,644 of them (23.7%) came back withchars empty — the wait elapsed and the sub-agent had produced nothing.
Declared wait time across all of them totals 35.7 hours. The most common
single form is a 30-second wait, used 3,127 times.
Why this is expensive
Each wait is a full billed turn. The model re-reads the accumulated session
context and receives an empty string. Because the context grows with the
session, the cost of one empty wait on day 11 is far higher than on day 1 — the
same non-event gets more expensive the longer the session lives.
The escalation is visible in the daily counts:
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
On the last day that session alone accounted for the bulk of my consumption,
and my client logged 379 usage_limit_exceeded errors that day. For scale: I
measured my weekly window as holding roughly 1.9-3.7B tokens, so a single day
of this is enough to exhaust one.
The session recorded only 8 errors in 11 days. Nothing failed, nothing
retried, nothing surfaced a warning — which is exactly why it ran for 11 days
without anyone noticing.
What I think the defect is
Three conditions combine:
- a session with no lifetime bound, kept alive for days;
- sub-agent coordination implemented as busy-waiting — the supervisor
polls with a blocking call instead of being notified on completion;
- sub-agents that routinely take longer than
yield_time_ms, which guarantees
the poll returns empty and will be repeated.
A poll that returns nothing should not cost a full context re-read. Either the
wait should not be modelled as a billed turn, or the supervisor should be woken
on completion rather than polling.
What would help, in the client
- Surface a warning when one session dominates account consumption, or when
priced calls repeat at a fixed interval — both are detectable client-side
from the rollout the CLI already writes.
- Expose empty-return waits in
/usageso the cost of coordination is visible
and separable from the cost of work.
- Bound session lifetime, or at least warn when a session has been open for
days and its per-turn context has grown past some threshold.
Related
#36313 and #36307 cover the weekly-window behaviour. This is a separate
problem: even with correct quota accounting, this pattern burns a week of
allowance on waiting.
Happy to share the extraction method — it only reads fields the CLI already
writes locally, and needs no access to my transcripts.
2 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
I traced this against the current rollout structure and shipped support for this pattern in the local auditor.
One precision point matters for the extraction:
write_stdin.arguments.charsis stdin sent into the running process. An empty value identifies a poll, but does not by itself prove that the poll returned no output.function_call_outputbycall_id, and call it empty only when the recognized tool wrapper has no payload afterOutput:.event_msg.token_count.info.last_token_usage. That gives recorded token usage for the model turn that issued the wait without claiming a separate backend "wait fee".yield_time_msis reported as requested timeout ceilings, not measured elapsed time.The browser-only parser now handles both
write_stdinandwait, streams large Codex rollouts locally, and sends only anonymized aggregates if a report is requested:https://mailcheck.agentcartai.com/tools/agent-cost-auditor/?platform=codex&utm_source=github&utm_medium=issue-reply&utm_campaign=codex-blocking-wait-36396
For comparing extraction methods, the useful questions are whether each of the 6,932 calls has a matching tool output and an adjacent
last_token_usage, and whether the 1,644 empties were classified from the output payload rather than the inputcharsfield. Please do not attach the raw rollout or prompts publicly.The local diagnosis is free. The optional 1 USDC Base checkout is only for a downloadable support-ready evidence pack.