Skip to content

fix(resilience): Grok Build OAuth 403 content refusal is a per-request rejection, not a ban (#14258) - #14259

Merged
diegosouzapw merged 2 commits into
diegosouzapw:release/v3.8.51from
sprintberlin:fix/grok-403-request-refusal
Sep 29, 2026
Merged

diegosouzapw merged 2 commits into
diegosouzapw:release/v3.8.51from
sprintberlin:fix/grok-403-request-refusal

Conversation

@sprintberlin

Copy link
Copy Markdown
Contributor

Summary

  • A Grok Build OAuth content refusal (403 "I can't help with that request.") was classified as FORBIDDEN and persisted as the terminal banned connection state. From then on every later request was answered locally with [grok-cli] All 1 connection(s) banned by upstream — please reconnect in the dashboard until an operator reconnected.
  • That 403 is a per-request safety refusal, not a dead credential: the same token answered 200 16 s before the event, a token refresh succeeded 3 s before it, and an in-flight call completed 200 2 s after it. The account was never banned by xAI.
  • Because grok-cli is authType: "oauth", it skipped the recoverable API-key 403 short-circuit and fell through to the generic OAuth FORBIDDEN default. The Anthropic carve-out from fix(sse): Anthropic OAuth 403 "Request not allowed" is a per-request refusal — cooldown with backoff instead of an instant ban (#12859) #12864 (Request not allowed) does not match this body.

Related Issues

Validation

  • Change type: routing / other (error classification)
  • Focused tests and category gates from the golden path
  • npx prettier --check and npx eslint on the changed files
  • Reconciled with the current active release base (release/v3.8.51 at 7a921299c5); focused checks rerun afterward
  • Production-code changes include a new or updated automated test in this PR
  • SonarQube is temporarily opt-in while the private project has no quota; it is not a PR gate.

Tests Added Or Updated

  • tests/unit/grok-request-refusal-not-a-ban.test.ts (new)
  • Neighbor suites rerun unchanged: tests/unit/anthropic-request-not-allowed-not-a-ban.test.ts, tests/unit/error-classifier.test.ts, tests/unit/request-rejected-streak.test.ts

Coverage Notes

  • The new test pins classifyProviderError() and resolveTerminalConnectionStatus() on the verbatim wrapped xAI body, the plain-text and JSON-wrapped variants, and a generic grok-cli 403 that must still remain FORBIDDEN.
  • Existing Anthropic refusal and classifier suites cover the sibling path and the rest of the 403 ladder.
  • Coverage should not drop: this is an additive classifier branch plus a new test file.

Reviewer Notes

@diegosouzapw

Copy link
Copy Markdown
Owner

This is a solid, well-evidenced fix — the request-refusal-vs-ban distinction is exactly right and the test pins both the new branch and a regression guard so a genuine account ban still resolves to banned. Before we merge: your CI run shows red across typecheck/eslint/docs-gates/all 4 unit shards, which is broader than the #13866 inherited base-red you flagged. Can you rebase onto the current release/v3.8.51 tip and re-run so we can confirm it's only the known #13866 set and nothing new?

@sprintberlin
sprintberlin force-pushed the fix/grok-403-request-refusal branch from 410ad96 to 100903b Compare September 21, 2026 15:55
@sprintberlin

Copy link
Copy Markdown
Contributor Author

Rebased onto the current release/v3.8.51 tip (893fef9). The two request-refusal tests pass locally and typecheck:core is clean. The remaining CI failures match the inherited #13866 base-red set.

@sprintberlin
sprintberlin force-pushed the fix/grok-403-request-refusal branch from 100903b to 858f462 Compare September 21, 2026 17:12
@sprintberlin

Copy link
Copy Markdown
Contributor Author

Re-synced onto the current release/v3.8.51 tip after #14290 landed (34113170). The two request-refusal tests still pass locally and typecheck:core is clean. Remaining CI failures continue to match the inherited #13866 base-red set.

…t rejection, not a ban

xAI's Grok Build upstream declines safety-sensitive prompts with
403 "I can't help with that request." — a refusal of THAT request, not
of the credential. Because grok-cli is an OAuth provider, the 403 fell
through to the generic FORBIDDEN classification and chatCore wrote the
terminal banned state, disabling the whole connection permanently.

Measured in production (2026-09-19): the same token answered 200 16 s
before the refusal, a token refresh succeeded 3 s before it, and an
in-flight call completed 200 2 s after it; the account was never banned
by xAI. The single refusal took the provider out of the tier-1 combo
for 16.75 h until a manual dashboard reconnect.

Same defect shape and same resolution as diegosouzapw#12859/diegosouzapw#12864 (Anthropic OAuth
"Request not allowed"): classify the verbatim refusal as
REQUEST_REJECTED before the terminal 403 fall-through. The existing
requestRejectedFailure leaf then applies the bounded cooldown ladder
(5 min, 15 min, terminal only on 3 consecutive refusals) and the
failing request falls through to the next combo target.

Closes diegosouzapw#14258
…tap files

Keeps tap.testFiles aligned with the new refusal regression suite
matching the pattern established in diegosouzapw#12864.
@sprintberlin
sprintberlin force-pushed the fix/grok-403-request-refusal branch from 858f462 to da476e7 Compare September 22, 2026 07:11
@sprintberlin

Copy link
Copy Markdown
Contributor Author

Rebased onto the current release/v3.8.51 tip (2b8f89a6, including the #14331 base-red drain and #14437 merge-train i18n gate fix). The focused Grok request-refusal test passes (2/2), and npm run typecheck:core is clean. This force-push has started a fresh CI run.

@diegosouzapw
diegosouzapw merged commit cc365a4 into diegosouzapw:release/v3.8.51 Sep 29, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(resilience): errorClassifier: Grok Build OAuth 403 "I can't help with that request." permanently bans a healthy grok-cli connection

2 participants