router-for-me/CLIProxyAPI · error

xai device code expired

Error message

xai device code expired

What it means

The local deadline computed for the device code (from its expires_in, with fallbacks) has passed and at least one poll attempt was already made, so PollForToken stops polling. This is the client-side expiry of the OAuth device code: the user never completed authorization in time.

Source

Thrown at internal/auth/xai/xai.go:238

	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)
		}
	}
}

// exchangeDeviceCode attempts to exchange a device code for tokens.
// Returns (token, error, nextInterval, shouldContinue).

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Restart the flow: request a fresh device code and complete authorization promptly
  2. Use verification_uri_complete (pre-filled code) or auto-open the browser to reduce user delay
  3. Verify the expires_in units and clock skew if codes seem to expire instantly
Defensive patterns

Strategy: retry

Try / catch

tokenData, err := auth.WaitForAuthorization(ctx, deviceCode)
if err != nil && strings.Contains(err.Error(), "device code expired") {
    log.Info("device code expired; requesting a new one")
    deviceCode, err = auth.RequestDeviceCode(ctx)
    if err != nil { return err }
    tokenData, err = auth.WaitForAuthorization(ctx, deviceCode)
}

Prevention

When it happens

Trigger: User does not visit the verification URI and enter the user_code before the code's expiry; authorization completes but the user takes longer than expires_in.

Common situations: Unattended/headless machine where the browser never opens; user steps away during the interactive step; expiry misparsed (e.g. expires_in in ms vs s) making the deadline too short.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/40322c584972afb8. Report an issue: GitHub.