diff --git a/hermes_cli/auth.py b/hermes_cli/auth.py index 021a537c023..8bab7674877 100644 --- a/hermes_cli/auth.py +++ b/hermes_cli/auth.py @@ -3443,12 +3443,34 @@ def refresh_xai_oauth_pure( ) if response.status_code != 200: detail = response.text.strip() + # ``403`` from xAI's token endpoint is almost always a tier / + # entitlement gate (the OAuth grant exists but the account isn't + # on the allowlist for API access). Re-running ``hermes model`` + # won't fix that — surface a separate error code so + # ``format_auth_error`` doesn't append a misleading + # re-authenticate hint, and point users at the ``XAI_API_KEY`` + # fallback. See #26847. + if response.status_code == 403: + raise AuthError( + "xAI token refresh failed with HTTP 403." + + (f" Response: {detail}" if detail else "") + + " This OAuth account is not authorized for xAI API" + " access — xAI may be restricting API/OAuth use to" + " specific SuperGrok tiers despite the in-app" + " subscription being active. Re-logging in won't" + " change that; set ``XAI_API_KEY`` and switch to" + " ``provider: xai`` (API-key path) if available, or" + " upgrade your subscription at https://x.ai/grok.", + provider="xai-oauth", + code="xai_oauth_tier_denied", + relogin_required=False, + ) raise AuthError( "xAI token refresh failed." + (f" Response: {detail}" if detail else ""), provider="xai-oauth", code="xai_refresh_failed", - relogin_required=(response.status_code in {400, 401, 403}), + relogin_required=(response.status_code in {400, 401}), ) try: payload = response.json() @@ -6225,6 +6247,25 @@ def _xai_oauth_exchange_code_for_tokens( if response.status_code != 200: body = response.text.strip() + # See ``refresh_xai_oauth_pure`` — token-exchange 403 also + # surfaces tier/entitlement gating from xAI's backend. Avoid + # the misleading "re-authenticate" hint and point at the API + # key fallback. See #26847. + if response.status_code == 403: + raise AuthError( + f"xAI token exchange failed (HTTP 403)." + + (f" Response: {body}" if body else "") + + " This OAuth account is not authorized for xAI API" + " access — xAI may be restricting API/OAuth use to" + " specific SuperGrok tiers despite the in-app" + " subscription being active. Set ``XAI_API_KEY``" + " and switch to ``provider: xai`` (API-key path) if" + " available, or upgrade your subscription at" + " https://x.ai/grok.", + provider="xai-oauth", + code="xai_oauth_tier_denied", + relogin_required=False, + ) raise AuthError( f"xAI token exchange failed (HTTP {response.status_code})." + (f" Response: {body}" if body else ""),