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

  1. Obtain a fresh session token from x.ai and retry the device flow.
  2. Check the token was copied completely (no leading/trailing whitespace or truncation).
  3. If it persists, log in via browser to confirm the account is in good standing, then extract a new token.
  4. 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

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

Related errors


AI-assisted analysis of Wei-Shaw/sub2api@073e92d171 (2026-08-15). Data as JSON: /api/errors/252cb04d707cb22c. Report an issue: GitHub.