You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
xAI policy 403 aborts Codex turns and retry loops the same user message #5277
xAI sometimes refuses a turn with HTTP 403 and the body I can't help with that request. instead of HTTP 200 + finish_reason: content_filter.
Codex treats that 403 as a transport failure. The user message is never recorded as a completed assistant turn. Retrying the same thread resends the same pending user text and the 403 repeats. A new Codex thread on the same account and xai/grok-4.6 succeeds.
Grok Build (native Grok CLI) does not get stuck: the refusal lands as a normal assistant message and the next user turn proceeds.
This is not a plan/subscription gate. Subscription 403s use different wording (need a Grok subscription, credits, not allowed to use this model). Those should stay errors.
Reproduction
Codex → OpenCodex → xai/grok-4.6 (OAuth or key).
In a long thread, send a request xAI's policy classifier refuses.
Observe unexpected status 403 Forbidden: Provider error 403: I can't help with that request., url: http://127.0.0.1:10100/v1/responses.
Retry / “continue” in the same thread → same 403 (~1s, no tokens).
New thread, trivial prompt → 200.
OpenCodex usage log for the failing request: status: 403, errorCode: permission_denied, upstreamError: Provider error 403: I can't help with that request., closeReason: non_stream.
Dashboard copy currently labels this as a plan/subscription gate. That label is wrong for this body.
Expected
The refusal is recorded as a completed (incomplete/content_filter) assistant turn. The next, different user message proceeds.
Actual
Codex surfaces a 403 transport error. The user message stays pending. Retries loop.
Version
OpenCodex 2.31.0 (reproduced). Latest 2.60.0 still maps this 403 through the generic error path (#5200 makes refusals terminal errors, which is correct for retry, but does not rewrite them into an in-band turn).
Proposed fix
Allowlist the refusal sentence. On HTTP 403 with that body, rewrite to a Chat Completions 200 with finish_reason: content_filter so the existing openai-chat adapter emits incomplete_details.reason = content_filter. Leave subscription/entitlement/model-access 403s unchanged.
Thanks for taking the time to submit this issue. It was closed automatically because it was not opened with a recognized issue template (Bug report, Feature request, Documentation, or Provider compatibility). Freeform and API-opened issues cannot skip this check.
This looks like a bug report but it does not use the Bug report template headings (for example Description/Log entry instead of Summary).
Please edit the issue to add:
Use the Bug report template, or edit this issue to include: Client or integration, Summary, Reproduction, Version, and Operating system.
Retitling with [Bug]: and applying the bug label alone is not enough without those section headings filled in.
Activity
github-actions commentedon Sep 20, 2026
Issue closed: use an issue template
Thanks for taking the time to submit this issue. It was closed automatically because it was not opened with a recognized issue template (Bug report, Feature request, Documentation, or Provider compatibility). Freeform and API-opened issues cannot skip this check.
Please edit the issue to add:
[Bug]:and applying thebuglabel alone is not enough without those section headings filled in.See the Contributing guide for details.
Once the report passes the automated checks, it will be reopened automatically unless a maintainer has changed its state.
sh940701 commentedon Sep 20, 2026
Reopened until the fix lands on dev. The PR should close this on merge.
fix(xai): surface model policy 403 as a completed content_filter turn
fix(xai): surface model policy 403 as a completed content_filter turn
fix(xai): bundle lane B — reasoning-model stop/penalty drops, policy …