router-for-me/CLIProxyAPI · warning
xai device code: context cancelled: %w
Error message
xai device code: context cancelled: %w
What it means
The context passed to PollForToken was cancelled or its deadline expired while the poll loop was waiting for user authorization. The select on ctx.Done() aborts the device flow and wraps context.Canceled or context.DeadlineExceeded.
Source
Thrown at internal/auth/xai/xai.go:235
}
deadline := time.Now().Add(MaxPollDuration)
if deviceCode.ExpiresIn > 0 {
codeDeadline := time.Now().Add(time.Duration(deviceCode.ExpiresIn) * time.Second)
if codeDeadline.Before(deadline) {
deadline = codeDeadline
}
}
// Poll immediately once, then wait between subsequent attempts.
firstAttempt := true
timer := time.NewTimer(0)
defer timer.Stop()
for {
select {
case <-ctx.Done():
return nil, fmt.Errorf("xai device code: context cancelled: %w", ctx.Err())
case <-timer.C:
if !firstAttempt && time.Now().After(deadline) {
return nil, fmt.Errorf("xai device code expired")
}
firstAttempt = false
token, pollErr, nextInterval, shouldContinue := a.exchangeDeviceCode(ctx, tokenEndpoint, deviceCode.DeviceCode, interval)
if token != nil {
return token, nil
}
if !shouldContinue {
return nil, pollErr
}
interval = nextInterval
timer.Reset(interval)
}
}
}View on GitHub (pinned to 78f0c4079e)
Solutions
- Give the device flow its own context with a timeout >= the device code lifetime (typically expires_in from the authorization response)
- Derive from context.Background() for interactive flows instead of a request-scoped context
- If cancellation was intentional (shutdown), treat this as a clean abort rather than an error
Example fix
// before tokenData, err := auth.WaitForAuthorization(reqCtx, deviceCode) // reqCtx has 30s deadline // after authCtx, cancel := context.WithTimeout(context.Background(), 15*time.Minute) defer cancel() tokenData, err := auth.WaitForAuthorization(authCtx, deviceCode)
Defensive patterns
Strategy: validation
Validate before calling
authCtx, cancel := context.WithTimeout(context.Background(), 15*time.Minute) // >= device code lifetime defer cancel() tokenData, err := auth.WaitForAuthorization(authCtx, deviceCode)
Try / catch
tokenData, err := auth.WaitForAuthorization(authCtx, deviceCode)
if err != nil {
if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
// intentional abort or timeout: clean exit, optionally restart flow
return err
}
return err
} Prevention
- Never reuse a short request-scoped context for an interactive device flow
- Size the context timeout to the device code expires_in plus margin
- Treat cancellation during shutdown as a clean abort, not a failure
When it happens
Trigger: Any cancellation of the ctx given to PollForToken/WaitForAuthorization during the polling window: user Ctrl-C, parent request timeout, shutdown of the calling service.
Common situations: A short HTTP request context reused for an interactive device flow that can legitimately take minutes; orchestrator (e.g. CLI command) timing out; graceful shutdown cancelling in-flight auth.
Related errors
- kimi: context cancelled: %w
- xai device code: response missing device_code
- xai device code: response missing user_code
- xai device code: response missing verification URI
- xai device code: response is nil
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/dc930fdc873861e7.
Report an issue: GitHub.