fix(xai-oauth): split 403 (tier/entitlement) from 400/401 in token endpoint
xAI's token endpoint returns HTTP 403 to the OAuth grant when the account isn't on the allowlist for API access (e.g. standard SuperGrok subscribers — see #26847). Treating it like a stale-token 400/401 made ``format_auth_error`` append "Run ``hermes model`` to re-authenticate", which is misleading because re-login can't change xAI's tier decision. Split 403 off in both ``refresh_xai_oauth_pure`` and the loopback login token exchange: * New error code ``xai_oauth_tier_denied`` with ``relogin_required=False`` * Message explains the entitlement gate and points at the ``XAI_API_KEY`` + ``provider: xai`` fallback * 400/401 still set ``relogin_required=True`` as before * 5xx still set ``relogin_required=False`` as before
This commit is contained in:
@@ -3443,12 +3443,34 @@ def refresh_xai_oauth_pure(
|
|||||||
)
|
)
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
detail = response.text.strip()
|
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(
|
raise AuthError(
|
||||||
"xAI token refresh failed."
|
"xAI token refresh failed."
|
||||||
+ (f" Response: {detail}" if detail else ""),
|
+ (f" Response: {detail}" if detail else ""),
|
||||||
provider="xai-oauth",
|
provider="xai-oauth",
|
||||||
code="xai_refresh_failed",
|
code="xai_refresh_failed",
|
||||||
relogin_required=(response.status_code in {400, 401, 403}),
|
relogin_required=(response.status_code in {400, 401}),
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
payload = response.json()
|
payload = response.json()
|
||||||
@@ -6225,6 +6247,25 @@ def _xai_oauth_exchange_code_for_tokens(
|
|||||||
|
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
body = response.text.strip()
|
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(
|
raise AuthError(
|
||||||
f"xAI token exchange failed (HTTP {response.status_code})."
|
f"xAI token exchange failed (HTTP {response.status_code})."
|
||||||
+ (f" Response: {body}" if body else ""),
|
+ (f" Response: {body}" if body else ""),
|
||||||
|
|||||||
Reference in New Issue
Block a user