fix(xai-oauth): rewrite entitlement-403 hint to not accuse subscribers (#26666)

PR #26644 confidently told users "xAI OAuth account lacks SuperGrok /
X Premium entitlement" on any 403 from xAI's permission-denied surface.
But that body is returned for at least four distinct causes that
Hermes cannot distinguish from the wire:

  * Account has no Grok subscription at all
  * Account has SuperGrok but the tier doesn't include the requested
    model (e.g. grok-4.3 needs SuperGrok Heavy)
  * Monthly quota for the subscribed tier is exhausted
  * SuperGrok is active but the API access add-on isn't enabled

Don Piedro pushed back that he IS subscribed yet still hit this.
Picking the worst-case interpretation ("you're not subscribed")
reads as wrong and insulting to subscribers, and points them at a
fix they already did.

New wording lists all 4 possibilities and points at
https://grok.com/?_s=usage where the user can check which applies.

The detection logic and credential-pool short-circuit (PR #26664)
are unchanged — only the user-facing wording is rephrased.
This commit is contained in:
Teknium 2026-05-15 17:15:22 -07:00 committed by GitHub
parent ce0e189d3e
commit 9818b9a1ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 69 additions and 17 deletions

View file

@ -5006,23 +5006,35 @@ class AIAgent:
@staticmethod @staticmethod
def _decorate_xai_entitlement_error(detail: str) -> str: def _decorate_xai_entitlement_error(detail: str) -> str:
"""Append a friendly hint when xAI's OAuth surface returns an """Append a neutral hint when xAI's OAuth surface returns the
entitlement-shaped error. permission-denied 403.
xAI's ``/v1/responses`` endpoint replies to OAuth tokens that lack a xAI's ``/v1/responses`` endpoint replies to several distinct failure
SuperGrok / X Premium subscription with HTTP 403 carrying a body like:: modes with the SAME body::
{"code": "The caller does not have permission to execute the {"code": "The caller does not have permission to execute the
specified operation", "error": "You have either run out of specified operation", "error": "You have either run out of
available resources or do not have an active Grok subscription. available resources or do not have an active Grok subscription.
Manage subscriptions at https://grok.com/..."} Manage subscriptions at https://grok.com/?_s=usage or subscribe
at https://grok.com/supergrok"}
The raw text is useful but the action the user needs to take (subscribe That body covers at least four real causes we cannot distinguish
on grok.com, or switch providers with ``/model``) isn't obvious from without more info from xAI:
the wire format. Detect the entitlement shape and append a hint.
Matched once per detail string won't double-decorate if the upstream * Account has no Grok subscription at all
already concatenated the same text. * Account has SuperGrok but the tier doesn't include the requested
model (e.g. grok-4.3 needs SuperGrok Heavy)
* Monthly quota for the subscribed tier is exhausted (the
``?_s=usage`` URL hints at this)
* SuperGrok is active but the API access add-on isn't enabled
Picking one ("you're not subscribed") is wrong for the other three
and reads as insulting to subscribers. Surface the raw xAI text
verbatim and point at https://grok.com/?_s=usage where the user
can see WHICH of those four it is.
Matched once per detail string won't double-decorate if the
upstream already concatenated the same text.
""" """
if not detail: if not detail:
return detail return detail
@ -5035,11 +5047,15 @@ class AIAgent:
if not is_entitlement: if not is_entitlement:
return detail return detail
hint = ( hint = (
" — xAI OAuth account lacks SuperGrok / X Premium entitlement for " " — xAI rejected the request on this OAuth account. Could be a "
"this model. Subscribe at https://grok.com or run `/model` to " "missing subscription, a tier that doesn't include this model, an "
"exhausted quota, or API access not enabled. Check "
"https://grok.com/?_s=usage to see which, or run `/model` to "
"switch providers." "switch providers."
) )
if hint.strip() in detail: # Idempotency: detect prior decoration by a substring unique to the
# hint (not present in xAI's own body text).
if "Could be a missing subscription" in detail:
return detail return detail
return f"{detail}{hint}" return f"{detail}{hint}"

View file

@ -163,7 +163,12 @@ def test_codex_stream_postlude_error_still_falls_back():
def test_summarize_api_error_decorates_xai_entitlement_403(): def test_summarize_api_error_decorates_xai_entitlement_403():
"""xAI's OAuth 403 must end with the subscribe-or-switch hint.""" """xAI's OAuth 403 must end with the neutral 4-cause hint.
Wording is deliberately ambiguous because xAI returns the SAME body for:
no subscription, wrong tier, exhausted quota, or API access not enabled.
Picking one (e.g. "you're not subscribed") would insult subscribers.
"""
from run_agent import AIAgent from run_agent import AIAgent
error = RuntimeError( error = RuntimeError(
@ -173,10 +178,39 @@ def test_summarize_api_error_decorates_xai_entitlement_403():
"subscriptions at https://grok.com'}" "subscriptions at https://grok.com'}"
) )
summary = AIAgent._summarize_api_error(error) summary = AIAgent._summarize_api_error(error)
# The original xAI text must survive — it's still useful diagnostic info.
assert "do not have an active Grok subscription" in summary assert "do not have an active Grok subscription" in summary
assert "SuperGrok" in summary # The hint must NOT confidently assert "lacks subscription"; it must
# acknowledge the 4 possible causes.
assert "Could be a missing subscription" in summary
assert "tier that doesn't include this model" in summary
assert "exhausted quota" in summary
assert "API access not enabled" in summary
# The hint must point at the usage page where the user can verify which.
assert "https://grok.com/?_s=usage" in summary
# Switching providers is still a valid escape hatch.
assert "/model" in summary assert "/model" in summary
assert "https://grok.com" in summary
def test_summarize_api_error_does_not_accuse_subscribers():
"""Hint must not confidently say the user has no subscription.
Don Piedro reported his subscription is active. The hint must not
contradict him it must list all 4 possible causes and let him
check which one applies.
"""
from run_agent import AIAgent
error = RuntimeError(
"HTTP 403: do not have an active Grok subscription"
)
summary = AIAgent._summarize_api_error(error)
# MUST NOT contain language that assumes the user is unsubscribed.
assert "lacks SuperGrok" not in summary
assert "lacks subscription" not in summary
assert "your account doesn't have" not in summary.lower()
# MUST contain the neutral framing.
assert "Could be" in summary or "could be" in summary
def test_summarize_api_error_decorates_xai_body_message(): def test_summarize_api_error_decorates_xai_body_message():
@ -197,7 +231,7 @@ def test_summarize_api_error_decorates_xai_body_message():
summary = AIAgent._summarize_api_error(_XaiErr("403")) summary = AIAgent._summarize_api_error(_XaiErr("403"))
assert "HTTP 403" in summary assert "HTTP 403" in summary
assert "SuperGrok / X Premium" in summary assert "Could be a missing subscription" in summary
def test_summarize_api_error_idempotent_for_entitlement_hint(): def test_summarize_api_error_idempotent_for_entitlement_hint():
@ -208,6 +242,8 @@ def test_summarize_api_error_idempotent_for_entitlement_hint():
once = AIAgent._decorate_xai_entitlement_error(raw) once = AIAgent._decorate_xai_entitlement_error(raw)
twice = AIAgent._decorate_xai_entitlement_error(once) twice = AIAgent._decorate_xai_entitlement_error(once)
assert once == twice assert once == twice
# Sanity: the hint did fire on the first pass.
assert "Could be a missing subscription" in once
def test_summarize_api_error_passes_through_unrelated_errors(): def test_summarize_api_error_passes_through_unrelated_errors():