docker/cli · info
login canceled
Error message
login canceled
What it means
Returned by LoginDevice (internal/oauth/manager/manager.go:131) when the context passed to the method is canceled during the device-token polling loop. The select statement hits the ctx.Done() branch, indicating the user or calling code aborted the login before tokens were received. This is expected/intentional behavior, not a system failure.
Solutions
- Handle this error as a normal cancellation, not a failure — check errors.Is(err, context.Canceled) and exit gracefully.
- If the cancellation is unexpected, investigate what is canceling the parent context (timeout too short, signal handling).
- Increase the context timeout if the user needs more time to complete browser-based authentication.
Example fix
// before: treating cancellation as a hard error
authCfg, err := mgr.LoginDevice(ctx, os.Stdout)
if err != nil {
log.Fatalf("login failed: %v", err)
}
// after: distinguish cancellation from real errors
authCfg, err := mgr.LoginDevice(ctx, os.Stdout)
if err != nil {
if errors.Is(err, context.Canceled) || err.Error() == "login canceled" {
fmt.Fprintln(os.Stderr, "login canceled by user")
os.Exit(1)
}
log.Fatalf("login failed: %v", err)
} Defensive patterns
Strategy: try-catch
Try / catch
authCfg, err := mgr.LoginDevice(ctx, w)
if err != nil {
if errors.Is(err, context.Canceled) || err.Error() == "login canceled" {
// User-initiated cancellation — exit gracefully, not an error
return nil
}
return err
} Prevention
- Treat 'login canceled' as expected user behavior, not a system error — exit gracefully.
- Use errors.Is(err, context.Canceled) to detect context-driven cancellation uniformly.
- If using a timeout context, size it generously (e.g., 5+ minutes) to allow browser-based auth.
- Avoid wrapping context.Canceled in other errors to keep cancellation detection clean.
When it happens
Trigger: During LoginDevice's blocking select (waiting for tokenResChan, waitForTokenErrChan, or ctx.Done), the context is canceled — user presses Ctrl+C, a parent context deadline expires, or the calling code programmatically cancels the context.
Common situations: User presses Ctrl+C at the 'Waiting for authentication in the browser' prompt, script/CI timeout cancels the context, signal handler triggers context cancellation, or the user simply decides not to complete browser login.
Related errors
- timed out waiting for device token
- unexpected response from tenant
- failed to start device code flow login
- builder prune has been cancelled
- conflicting options: cannot specify both --host and…
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/a5fecd5606a0c6ef.
Report an issue: GitHub.
Appendix: source
Thrown at internal/oauth/manager/manager.go:131
tokenRes, err := m.api.WaitForDeviceToken(ctx, state)
if err != nil {
waitForTokenErrChan <- err
return
}
tokenResChan <- tokenRes
}()
go func() {
reader := bufio.NewReader(os.Stdin)
_, _ = reader.ReadString('\n')
_ = m.openBrowser(state.VerificationURI)
}()
_, _ = fmt.Fprint(w, "\nWaiting for authentication in the browser…\n")
var tokenRes api.TokenResponse
select {
case <-ctx.Done():
return nil, errors.New("login canceled")
case err := <-waitForTokenErrChan:
return nil, fmt.Errorf("failed waiting for authentication: %w", err)
case tokenRes = <-tokenResChan:
}
claims, err := oauth.GetClaims(tokenRes.AccessToken)
if err != nil {
return nil, fmt.Errorf("failed to parse token claims: %w", err)
}
err = m.storeTokensInStore(tokenRes, claims.Domain.Username)
if err != nil {
return nil, fmt.Errorf("failed to store tokens: %w", err)
}
pat, err := m.api.GetAutoPAT(ctx, m.audience, tokenRes)
if err != nil {
return nil, errView on GitHub (pinned to 4f84911bfe)