fix(xai): drop stale X Premium+ hint from entitlement 403 surfacing (#27110)

xAI announced on 2026-05-16 (https://x.ai/news/grok-hermes) that X Premium
subscriptions now work in Hermes Agent. The hint we shipped in PR #26644
asserted the opposite ("X Premium+ does NOT include xAI API access — only
standalone SuperGrok subscribers can use this provider"), which would now
misdirect Premium+ users who hit any other 403 (no Grok sub at all, wrong
tier, exhausted quota) into thinking they need to switch subscriptions
when their sub is in fact valid.

Remove _decorate_xai_entitlement_error and its two call sites in
_summarize_api_error. xAI's own body text already says "Manage subscriptions
at https://grok.com/?_s=usage" — surface that verbatim and let xAI's wording
do the diagnosis.

The _is_entitlement_failure guard (which prevents credential-pool refresh
loops on entitlement 403s) and the reasoning-replay gating for xai-oauth
are unrelated and untouched.

Update tests to assert the body still surfaces verbatim and that no
Hermes-side editorializing is appended.
This commit is contained in:
Teknium 2026-05-16 16:00:01 -07:00 committed by GitHub
parent fb05f5d4b5
commit dffb602f37
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 24 additions and 118 deletions

View file

@ -5046,63 +5046,6 @@ class AIAgent:
return True
return False
@staticmethod
def _decorate_xai_entitlement_error(detail: str) -> str:
"""Append a neutral hint when xAI's OAuth surface returns the
permission-denied 403.
xAI's ``/v1/responses`` endpoint replies to several distinct failure
modes with the SAME body::
{"code": "The caller does not have permission to execute the
specified operation", "error": "You have either run out of
available resources or do not have an active Grok subscription.
Manage subscriptions at https://grok.com/?_s=usage or subscribe
at https://grok.com/supergrok"}
That body covers several real causes we cannot distinguish without
more info from xAI. The most common (and least obvious) one is
that **X Premium+ does NOT include API access** only standalone
SuperGrok subscribers can use Hermes against xai-oauth. Lots of
users see Grok in their X app, assume it works here too, and hit
this 403 with no idea why. Lead the hint with that.
Other possible causes:
* No Grok subscription at all
* SuperGrok tier doesn't include the requested model (e.g.
grok-4.3 may need a higher tier)
* Monthly quota exhausted (the ``?_s=usage`` URL hints at this)
Surface the raw xAI text verbatim and point at
https://grok.com/?_s=usage where the user can see WHICH applies.
Matched once per detail string won't double-decorate if the
upstream already concatenated the same text.
"""
if not detail:
return detail
lower = detail.lower()
is_entitlement = (
"do not have an active grok subscription" in lower
or ("out of available resources" in lower and "grok" in lower)
or ("does not have permission" in lower and "grok" in lower)
)
if not is_entitlement:
return detail
hint = (
" — xAI rejected this OAuth account. NOTE: X Premium+ does NOT "
"include xAI API access — only standalone SuperGrok subscribers "
"can use this provider. Other possible causes: no Grok "
"subscription, your tier doesn't include this model, or your "
"quota is exhausted. Check https://grok.com/?_s=usage to see "
"which, or run `/model` to switch providers."
)
# Idempotency: detect prior decoration by a substring unique to the
# hint (not present in xAI's own body text).
if "X Premium+ does NOT include" in detail:
return detail
return f"{detail}{hint}"
@staticmethod
def _summarize_api_error(error: Exception) -> str:
"""Extract a human-readable one-liner from an API error.
@ -5142,12 +5085,12 @@ class AIAgent:
if msg:
status_code = getattr(error, "status_code", None)
prefix = f"HTTP {status_code}: " if status_code else ""
return AIAgent._decorate_xai_entitlement_error(f"{prefix}{msg[:300]}")
return f"{prefix}{msg[:300]}"
# Fallback: truncate the raw string but give more room than 200 chars
status_code = getattr(error, "status_code", None)
prefix = f"HTTP {status_code}: " if status_code else ""
return AIAgent._decorate_xai_entitlement_error(f"{prefix}{raw[:500]}")
return f"{prefix}{raw[:500]}"
def _mask_api_key_for_logs(self, key: Optional[str]) -> Optional[str]:
if not key: