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.

View original on GitHub ↗

2 Comments

github-actions[bot] contributor · 19 days ago

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

  • #36562

Powered by Codex Action

jdcodes1 · 9 days ago

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 as error.code (session_budget_exceeded / your rollout_budget_exhausted) is a serialization change, not new plumbing.

The meter value is the genuinely missing piece: the variant carries no payload, and RolloutBudget keeps weighted_tokens_used internally (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 a usage-adjacent object on turn.failed — would make the terminal event self-contained. Given exec --json is explicitly the orchestrator surface, prose-only terminal errors are a contract gap worth fixing generally: every CodexErrorDetails discriminant could ship as a stable code field for the same reason.