docker/cli · error

failed to start device code flow login

Error message

failed to start device code flow login

What it means

ErrDeviceLoginStartFail (internal/oauth/manager/manager.go:76) is returned by LoginDevice when the initial call to GetDeviceCode fails for any reason, or when the tenant response is missing the UserCode field. The actual underlying error is only logged at debug level via logrus.Debugf and is not exposed to the caller, making this a deliberately opaque sentinel error.

Solutions

  1. Enable debug logging (DOCKER_CLI_DEBUG=1 or logrus debug level) to see the underlying GetDeviceCode error printed alongside 'failed to start device code login'.
  2. Verify network connectivity to the tenant URL: ensure DNS resolves and the host is reachable on port 443.
  3. Check that ClientID, Audience, and Tenant in OAuthManagerOptions are correctly configured for your Auth0 application.
  4. Fall back to token-based login (docker login -u <username>) if the device-code flow endpoint is unavailable.

Example fix

// before: opaque error with no detail
authCfg, err := mgr.LoginDevice(ctx, os.Stdout)
if err != nil {
    return err // 'failed to start device code flow login' — no detail
}

// after: enable debug logging to expose root cause
logrus.SetLevel(logrus.DebugLevel)
authCfg, err := mgr.LoginDevice(ctx, os.Stdout)
if err != nil {
    if errors.Is(err, manager.ErrDeviceLoginStartFail) {
        // check debug logs for 'failed to start device code login: <detail>'
        return fmt.Errorf("device login unavailable; check network and tenant config")
    }
    return err
}
Defensive patterns

Strategy: retry

Validate before calling

// Pre-flight check before LoginDevice
func canReachTenant(ctx context.Context, tenant string) error {
    u := "https://" + tenant + "/oauth/device/code"
    req, _ := http.NewRequestWithContext(ctx, "POST", u, strings.NewReader(""))
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return fmt.Errorf("cannot reach tenant %s: %w", tenant, err)
    }
    defer resp.Body.Close()
    // Any HTTP response (even 4xx) means the tenant is reachable
    return nil
}

Type guard

// Check if the error is specifically the device-login-start sentinel
func isDeviceLoginStartFail(err error) bool {
    return errors.Is(err, manager.ErrDeviceLoginStartFail)
}

Try / catch

authCfg, err := mgr.LoginDevice(ctx, w)
if err != nil {
    if errors.Is(err, manager.ErrDeviceLoginStartFail) {
        // Enable debug logging and retry; underlying error is only in debug logs
        logrus.SetLevel(logrus.DebugLevel)
        return fmt.Errorf("device login failed to start (enable debug for details); try 'docker login -u <user>' as fallback")
    }
    return err
}

Prevention

When it happens

Trigger: LoginDevice calls m.api.GetDeviceCode(ctx, m.audience) which returns an error (network failure, DNS resolution failure, TLS error, HTTP error from tenant), OR GetDeviceCode succeeds but state.UserCode is an empty string. In both cases the sentinel ErrDeviceLoginStartFail is returned.

Common situations: No network connectivity to the Auth0 tenant host, wrong ClientID or Audience configured, tenant URL unreachable behind a firewall, clock skew causing TLS handshake failure, or a tenant misconfiguration that returns a device-code response without a user_code field.

Related errors


AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07). Data as JSON: /api/errors/8229dc91c27b85ec. Report an issue: GitHub.

Appendix: source

Thrown at internal/oauth/manager/manager.go:76

		browser.Stderr = io.Discard
		openBrowser = browser.OpenURL
	}

	return &OAuthManager{
		clientID: options.ClientID,
		audience: options.Audience,
		tenant:   options.Tenant,
		store:    options.Store,
		api: api.API{
			TenantURL: "https://" + options.Tenant,
			ClientID:  options.ClientID,
			Scopes:    scopes,
		},
		openBrowser: openBrowser,
	}
}

var ErrDeviceLoginStartFail = errors.New("failed to start device code flow login")

// LoginDevice launches the device authentication flow with the tenant,
// printing instructions to the provided writer and attempting to open the
// browser for the user to authenticate.
// After the user completes the browser login, LoginDevice uses the retrieved
// tokens to create a Hub PAT which is returned to the caller.
// The retrieved tokens are stored in the credentials store (under a separate
// key), and the refresh token is concatenated with the client ID.
func (m *OAuthManager) LoginDevice(ctx context.Context, w io.Writer) (*types.AuthConfig, error) {
	state, err := m.api.GetDeviceCode(ctx, m.audience)
	if err != nil {
		logrus.Debugf("failed to start device code login: %v", err)
		return nil, ErrDeviceLoginStartFail
	}

	if state.UserCode == "" {
		logrus.Debugf("failed to start device code login: missing user code")
		return nil, ErrDeviceLoginStartFail

View on GitHub (pinned to 4f84911bfe)