codex exec --json omits usage and a typed code on rollout-budget exhaustion
Open 💬 2 comments Opened Aug 9, 2026 by joshrotenberg
💡 Likely answer: A maintainer (github-actions[bot], contributor)
responded on this thread — see the highlighted reply below.
Summary
When the experimental native features.rollout_budget stops a codex exec --json turn, Codex emits a turn.failed event containing only the English message shared rollout token budget exhausted. The event has no usage object or machine-readable error code, even though the internal rollout meter necessarily had a value to make the decision.
This makes it possible for an orchestrator to recognize the limit only by matching prose, and impossible to persist the usage that was available inside Codex at termination.
Reproduction
codex exec --json --ephemeral --ignore-user-config --ignore-rules \
--skip-git-repo-check \
-c 'features.rollout_budget={enabled=true,limit_tokens=1,reminder_at_remaining_tokens=[],sampling_token_weight=1.0,prefill_token_weight=1.0}' \\\n 'Reply with exactly: ok'\n```\n\n### Observed JSONL\n\n```jsonl\n{"type":"thread.started","thread_id":"<redacted>"}\n{"type":"item.completed","item":{"id":"item_0","type":"error","message":"Under-development features enabled: rollout_budget. ..."}}\n{"type":"turn.started"}\n{"type":"item.completed","item":{"id":"item_1","type":"agent_message","text":"ok"}}\n{"type":"error","message":"shared rollout token budget exhausted"}\n{"type":"turn.failed","error":{"message":"shared rollout token budget exhausted"}}\n```\n\nThe process exits 1. There is no `usage` on the terminal event.\n\n### Expected\n\nThe terminal event should expose:\n\n- a stable machine-readable kind/code such as `rollout_budget_exhausted`; and\n- the final rollout-budget units and available token usage used for the enforcement decision (while preserving the existing message for humans).\n\nA dedicated meter value is important because rollout-budget units are not necessarily portable input+output tokens: Codex can use provider-reported `codex_rollout_budget_units`, otherwise its configured weighted fallback.\n\n### Environment\n\n- `codex-cli 0.145.0`\n- macOS Darwin 25.6.0 arm64\n\nRelated but distinct from #37138: that issue covers a provider response missing usage causing the budget not to advance; this report covers the JSONL terminal after the native budget does advance and stops the turn.
2 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Half of what you're asking for already exists internally and is lost at the serialization boundary: the budget stop is a typed error variant —
CodexErrorDetails::SessionBudgetExceeded(payload-free, used as the analytics discriminant: https://github.com/openai/codex/blob/1f41cc5d92/codex-rs/protocol/src/error.rs#L85-L86) — but the exec JSONL emitter flattens it to the Display string, so orchestrators get prose where codex itself has an enum. Exposing the discriminant aserror.code(session_budget_exceeded/ yourrollout_budget_exhausted) is a serialization change, not new plumbing.The meter value is the genuinely missing piece: the variant carries no payload, and
RolloutBudgetkeepsweighted_tokens_usedinternally (core/src/rollout_budget.rs#L63, with the provider-units-vs-weighted-fallback distinction you note at#L55-L62). Attaching{units_used, limit_tokens, units_source}at the point the exceeded decision is made — either as payload on the variant or as ausage-adjacent object onturn.failed— would make the terminal event self-contained. Given exec--jsonis explicitly the orchestrator surface, prose-only terminal errors are a contract gap worth fixing generally: everyCodexErrorDetailsdiscriminant could ship as a stablecodefield for the same reason.