fix(xai-oauth): lead entitlement-403 hint with X Premium+ gotcha (#26672)

The #1 confusing cause of the xAI 403 (per Teknium): X Premium+
subscribers see Grok inside the X app and assume API access is
included.  It is NOT — only standalone SuperGrok subscribers can use
xai-oauth with Hermes today.  Without calling this out, every Premium+
user hits the 403 with no idea why.

PR #26666's neutral 4-cause list was correct but buried the most
common cause.  Lead with the Premium+ gotcha, then list the other
possibilities (no subscription, wrong tier, exhausted quota) as
fallbacks.  Same neutral framing — does not accuse anyone of being
unsubscribed.
This commit is contained in:
Teknium 2026-05-15 17:23:33 -07:00 committed by GitHub
parent 9818b9a1ac
commit 6784c80794
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 44 additions and 38 deletions

View file

@ -163,11 +163,13 @@ def test_codex_stream_postlude_error_still_falls_back():
def test_summarize_api_error_decorates_xai_entitlement_403():
"""xAI's OAuth 403 must end with the neutral 4-cause hint.
"""xAI's OAuth 403 must surface the X Premium+ gotcha + neutral causes.
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.
Wording deliberately leads with the X Premium+ gotcha because that's
the #1 confusing case: people see Grok in their X app, assume it
works here too, and hit this 403 with no idea API access is a
separate SKU. Other causes (no subscription, wrong tier, exhausted
quota) follow.
"""
from run_agent import AIAgent
@ -180,13 +182,15 @@ def test_summarize_api_error_decorates_xai_entitlement_403():
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
# 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.
# The hint MUST lead with the X Premium+ gotcha (most likely cause
# for users who think they're subscribed).
assert "X Premium+ does NOT include" in summary
assert "standalone SuperGrok subscribers" in summary
# Other causes still listed.
assert "no Grok subscription" in summary
assert "tier doesn't include this model" in summary
assert "quota is exhausted" in summary
# The hint must point at the usage page where the user can verify.
assert "https://grok.com/?_s=usage" in summary
# Switching providers is still a valid escape hatch.
assert "/model" in summary
@ -196,8 +200,9 @@ 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.
contradict him leading with the X Premium+ gotcha gives subscribers
a plausible reason ("oh, I'm on Premium+ not pure SuperGrok") instead
of accusing them of lying about having a subscription.
"""
from run_agent import AIAgent
@ -205,12 +210,11 @@ def test_summarize_api_error_does_not_accuse_subscribers():
"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.
# MUST NOT contain language that flatly 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
assert "you are not subscribed" not in summary.lower()
# MUST lead with the most-likely-but-non-accusatory cause.
assert "X Premium+ does NOT include" in summary
def test_summarize_api_error_decorates_xai_body_message():
@ -231,7 +235,7 @@ def test_summarize_api_error_decorates_xai_body_message():
summary = AIAgent._summarize_api_error(_XaiErr("403"))
assert "HTTP 403" in summary
assert "Could be a missing subscription" in summary
assert "X Premium+ does NOT include" in summary
def test_summarize_api_error_idempotent_for_entitlement_hint():
@ -243,7 +247,7 @@ def test_summarize_api_error_idempotent_for_entitlement_hint():
twice = AIAgent._decorate_xai_entitlement_error(once)
assert once == twice
# Sanity: the hint did fire on the first pass.
assert "Could be a missing subscription" in once
assert "X Premium+ does NOT include" in once
def test_summarize_api_error_passes_through_unrelated_errors():