charmbracelet/crush · warning
authorization timed out
Error message
authorization timed out
What it means
PollForToken polls the token endpoint until the device code expires; if the polling loop finishes without receiving a token (interval budget exhausted), it returns the sentinel "authorization timed out". The device code's expiresIn seconds elapsed while the user never completed browser authorization.
Source
Thrown at internal/oauth/copilot/oauth.go:97
case <-ticker.C:
}
token, err := tryGetToken(ctx, dc.DeviceCode)
if err == errPending {
continue
}
if err == errSlowDown {
interval += 5
ticker.Reset(time.Duration(interval) * time.Second)
continue
}
if err != nil {
return nil, err
}
return token, nil
}
return nil, fmt.Errorf("authorization timed out")
}
var (
errPending = fmt.Errorf("pending")
errSlowDown = fmt.Errorf("slow_down")
)
func tryGetToken(ctx context.Context, deviceCode string) (*oauth.Token, error) {
data := url.Values{}
data.Set("client_id", clientID)
data.Set("device_code", deviceCode)
data.Set("grant_type", "urn:ietf:params:oauth:grant-type:device_code")
req, err := http.NewRequestWithContext(ctx, "POST", accessTokenURL, strings.NewReader(data.Encode()))
if err != nil {
return nil, err
}
req.Header.Set("Accept", "application/json")View on GitHub (pinned to 7944b8e522)
Solutions
- Restart the login flow to get a fresh device code and complete authorization promptly
- Open the verification URL in the default browser immediately and enter the code before expiry
- Increase the effective deadline by restarting if your workflow is slow; report if expires_in is suspiciously short
- Automate the browser-open step so no manual URL copying delays authorization
Example fix
// before
tok, err := PollForToken(ctx, dc)
// after
tok, err := PollForToken(ctx, dc)
if err != nil && err.Error() == "authorization timed out" {
// restart device flow for a fresh code
dc, _ = RequestDeviceCode(ctx)
tok, err = PollForToken(ctx, dc)
} Defensive patterns
Strategy: try-catch
Validate before calling
if time.Since(dc.CreatedAt) > time.Duration(dc.ExpiresIn)*time.Second {
return fmt.Errorf("device code already expired; restart login")
} Try / catch
tok, err := PollForToken(ctx, dc)
if err != nil && err.Error() == "authorization timed out" {
dc, _ = RequestDeviceCode(ctx)
tok, err = PollForToken(ctx, dc)
} Prevention
- Open the verification URL in the browser automatically at flow start
- Warn the user how long the code remains valid
- Restart the device flow automatically when expiry is imminent and nothing is pending
When it happens
Trigger: The loop's elapsed time exceeds the device code's expires_in value before the user completes login; each iteration returns errPending/errSlowDown until the deadline passes.
Common situations: User is away from the machine or ignores the browser prompt; user takes longer than the ~15 minute expiry; slow_down responses repeatedly lengthen the interval so fewer attempts fit before expiry.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- device code request failed: %s - %s
- authorization failed: %s
- ${ErrorDescription}
- copilot token request failed: %s - %s
- create request: %w
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/08413936e6d7c739.
Report an issue: GitHub.