docker/cli · warning
timed out waiting for device token
Error message
timed out waiting for device token
What it means
ErrTimeout, returned by WaitForDeviceToken when the device-code OAuth flow's expiry timer fires before the user completed authentication in their browser. Auth0 grants a limited window (ExpiryDuration from the device-code response) to authorize the device; if that elapses, polling stops and this error is returned.
Solutions
- Re-initiate the login flow to get a fresh device code and complete browser auth promptly.
- Open the verification URL and enter the code immediately after it is displayed.
- If expiry is consistently too short, check network connectivity and that the browser can reach the tenant.
Example fix
# before: docker login # device code shown, user waited too long -> ErrTimeout # after: docker login # re-run; open the printed URL and authorize within the time window
Defensive patterns
Strategy: retry
Validate before calling
// Before starting the device flow, you cannot extend the tenant expiry,
// but you can guard against background contexts that cancel early:
func ensureLoginContext(ctx context.Context) (context.Context, context.CancelFunc) {
return context.WithTimeout(ctx, 15*time.Minute)
} Type guard
// Detect the timeout sentinel
func isDeviceTokenTimeout(err error) bool {
return errors.Is(err, api.ErrTimeout)
} Try / catch
// Re-initiate the device flow on timeout (user-driven retry)
state, err := api.GetDeviceCode(ctx, audience)
if err != nil { return err }
tok, err := api.WaitForDeviceToken(ctx, state)
if errors.Is(err, api.ErrTimeout) {
// prompt user to retry promptly
state, err = api.GetDeviceCode(ctx, audience)
if err != nil { return err }
tok, err = api.WaitForDeviceToken(ctx, state)
}
return tok, err Prevention
- Open the verification URL and authorize the device immediately after the code is displayed.
- Re-run the login flow if the code expires; do not reuse a stale device code.
- Ensure the browser can reach the tenant and the user completes auth within the expiry window.
When it happens
Trigger: Initiating a device-code login (e.g. docker login with the OAuth flow), receiving the device code and URL, but not visiting the URL / entering the code before the tenant's expiry window closes. The timeout.C branch at api.go:136-138 fires.
Common situations: User stepped away from the terminal; the device code expired before the browser auth completed; slow network or browser delays; the verification URL was not opened in time.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- unexpected response from tenant
- failed to start device code flow login
- login canceled
- failed to get tokens
- failed to decode response
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/4ebe3e4a5926137d.
Report an issue: GitHub.
Appendix: source
Thrown at internal/oauth/api/api.go:50
// ClientID is the client ID for the application to auth with the tenant.
ClientID string
// Scopes are the scopes that are requested during the device auth flow.
Scopes []string
}
// TokenResponse represents the response of the /oauth/token route.
type TokenResponse struct {
AccessToken string `json:"access_token"`
IDToken string `json:"id_token"`
RefreshToken string `json:"refresh_token"`
Scope string `json:"scope"`
ExpiresIn int `json:"expires_in"`
TokenType string `json:"token_type"`
Error *string `json:"error,omitempty"`
ErrorDescription string `json:"error_description,omitempty"`
}
var ErrTimeout = errors.New("timed out waiting for device token")
// GetDeviceCode initiates the device-code auth flow with the tenant.
// The state returned contains the device code that the user must use to
// authenticate, as well as the URL to visit, etc.
func (a API) GetDeviceCode(ctx context.Context, audience string) (State, error) {
data := url.Values{
"client_id": {a.ClientID},
"audience": {audience},
"scope": {strings.Join(a.Scopes, " ")},
}
deviceCodeURL := a.TenantURL + "/oauth/device/code"
resp, err := postForm(ctx, deviceCodeURL, strings.NewReader(data.Encode()))
if err != nil {
return State{}, err
}
defer func() {
_ = resp.Body.Close()View on GitHub (pinned to 4f84911bfe)