Wei-Shaw/sub2api · error
xai sso unauthorized
Error message
xai sso unauthorized
What it means
ErrSSOUnauthorized is a sentinel error in the xAI SSO device flow indicating the session token used to drive the browser-side device authorization is not authorized (the headless HTTP flow got a 401-class rejection or the token was rejected downstream). It is matched with errors.Is by callers to distinguish 'bad/expired session' from network or flow-shape failures.
Source
Thrown at backend/internal/pkg/xai/sso_device.go:35
)
const (
SSOBuildScope = "openid profile email offline_access grok-cli:access api:access conversations:read conversations:write"
SSOAccountsURL = "https://accounts.x.ai/"
SSODeviceURL = OAuthIssuer + "/oauth2/device/code"
SSOVerifyURL = OAuthIssuer + "/oauth2/device/verify"
SSOApproveURL = OAuthIssuer + "/oauth2/device/approve"
SSOTokenURL = OAuthIssuer + "/oauth2/token"
SSOConversionTimeout = 90 * time.Second
ssoMaxAuthBody = 2 << 20
ssoMaxTokenLength = 16 << 10
ssoDefaultUA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
ssoDefaultTokenTTL = 6 * time.Hour
)
var (
ErrSSOUnauthorized = errors.New("xai sso unauthorized")
ErrSSOAuthorizationDenied = errors.New("xai device authorization denied")
)
type SSOHTTPError struct{ Status int }
func (e SSOHTTPError) Error() string { return fmt.Sprintf("xAI OAuth HTTP %d", e.Status) }
type SSODeviceHTTPClient interface {
Do(*http.Request) (*http.Response, error)
}
type SSODeviceOptions struct {
HTTPClient SSODeviceHTTPClient
UserAgent string
Sleep func(context.Context, time.Duration) error
}
type ssoDeviceFlow struct {View on GitHub (pinned to 073e92d171)
Solutions
- Obtain a fresh session token from x.ai and retry the device flow.
- Check the token was copied completely (no leading/trailing whitespace or truncation).
- If it persists, log in via browser to confirm the account is in good standing, then extract a new token.
- Match with errors.Is(err, xai.ErrSSOUnauthorized) in the caller to surface a 'please re-authenticate' UX instead of a generic error.
Example fix
// before
if err != nil {
return fmt.Errorf("sso failed: %w", err) // user sees opaque error
}
// after
if errors.Is(err, xai.ErrSSOUnauthorized) {
return errors.New("session token is invalid or expired; please provide a new one")
} Defensive patterns
Strategy: try-catch
Try / catch
if err := flow.Start(ctx); err != nil {
if errors.Is(err, xai.ErrSSOUnauthorized) {
// session token dead: prompt user for a new token, do not retry with the same one
return ErrReauthRequired
}
return err
} Prevention
- Check token freshness before starting the flow
- Store tokens with their acquisition time and refresh proactively
- Map the sentinel to a 're-authenticate' UX so users self-serve
When it happens
Trigger: Starting an xAI SSO device login with a missing, expired, or revoked session token; the accounts.x.ai endpoints respond 401 during Start, and callers translate that to this sentinel. Also returned by token polling paths when the session becomes invalid mid-flow.
Common situations: User pasted an SSO token that has expired or was rotated; the token was truncated when copied; the account requires re-login on x.ai; tokens invalidated by a password change or security reset.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- xAI device flow response is incomplete
- xAI device verification did not reach consent page
- xAI device approval did not reach done page
- xAI device flow token polling timed out
- xAI OAuth response exceeds 2 MiB
AI-assisted analysis of Wei-Shaw/sub2api@073e92d171 (2026-08-15).
Data as JSON: /api/errors/252cb04d707cb22c.
Report an issue: GitHub.