Wei-Shaw/sub2api · error
xAI device flow token polling timed out
Error message
xAI device flow token polling timed out
What it means
pollToken loops POST-ing the device_code to the token endpoint until the deadline, which is capped at 75 seconds regardless of the advertised expires_in. If the deadline passes without an authorization_pending turning into a token, this timeout error is returned. Slow users (MFA, distracted) routinely exceed 75s on a flow xAI advertises as valid for ~30 minutes.
Source
Thrown at backend/internal/pkg/xai/sso_device.go:241
Scope: payload.Scope,
}, nil
}
switch payload.Error {
case "authorization_pending":
continue
case "slow_down":
interval += 5 * time.Second
continue
case "access_denied", "expired_token":
return nil, ErrSSOAuthorizationDenied
default:
if status >= 400 {
return nil, fmt.Errorf("xAI token polling failed (%s): %w", firstNonEmpty(payload.ErrorDescription, payload.Error), SSOHTTPError{Status: status})
}
return nil, fmt.Errorf("xAI token polling failed: %s", firstNonEmpty(payload.ErrorDescription, payload.Error, strconv.Itoa(status)))
}
}
return nil, errors.New("xAI device flow token polling timed out")
}
func (f *ssoDeviceFlow) do(ctx context.Context, method, endpoint string, form url.Values) (int, string, []byte, error) {
if !safeXAIAuthURL(endpoint) {
return 0, "", nil, errors.New("xAI OAuth URL is not trusted")
}
currentURL := endpoint
currentMethod := method
currentForm := form
for redirects := 0; redirects <= 8; redirects++ {
var body io.Reader
if currentForm != nil {
body = strings.NewReader(currentForm.Encode())
}
request, err := http.NewRequestWithContext(ctx, currentMethod, currentURL, body)
if err != nil {
return 0, currentURL, nil, err
}View on GitHub (pinned to 073e92d171)
Solutions
- Approve the device prompt promptly (within ~75 seconds) and retry.
- Verify step [66] genuinely reached the 'done' page; if approval silently failed, polling will always time out.
- If you maintain this code, raise the minDuration cap (75*time.Second) to a more realistic bound for interactive users.
- Treat this error as retryable at the orchestration layer: restart the whole device flow rather than re-polling a dead device_code.
Example fix
// before deadline := time.Now().Add(minDuration(expiresIn, 75*time.Second)) // after (if interactive approval is expected) deadline := time.Now().Add(minDuration(expiresIn, 5*time.Minute))
Defensive patterns
Strategy: retry
Try / catch
tok, err := flow.Token(ctx)
if err != nil && strings.Contains(err.Error(), "token polling timed out") {
// device_code is now spent; only a full restart can succeed
tok, err = runFullDeviceFlow(ctx)
} Prevention
- Approve the browser prompt within ~75s of starting
- Never re-poll with the same device_code after timeout
- If you own the code, size the deadline to real human approval times
When it happens
Trigger: User takes longer than min(expires_in, 75s) to approve; or xAI keeps answering authorization_pending because the approval in step [66] did not actually register. Also hit when the caller's ctx is fine but approval simply never happened.
Common situations: Automated flows with no human watching; approval done but cookies lost so xAI still reports pending; users who step away from the browser; CI runs that cannot approve at all.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
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 sso unauthorized
- xAI OAuth response exceeds 2 MiB
AI-assisted analysis of Wei-Shaw/sub2api@073e92d171 (2026-08-15).
Data as JSON: /api/errors/19ecf076a011d6e0.
Report an issue: GitHub.