sipeed/picoclaw · warning

authentication timed out after 5 minutes

Error message

authentication timed out after 5 minutes

What it means

The whole interactive login window expired: neither the loopback browser callback nor the manual-paste channel delivered a code within 5 minutes (oauth.go:183, time.After(5 * time.Minute)). The OAuth code is short-lived anyway, so after this timeout the flow must restart from a fresh PKCE/state pair.

Source

Thrown at pkg/auth/oauth.go:183

		return ExchangeCodeForTokens(cfg, result.code, pkce.CodeVerifier, redirectURI)
	case manualInput := <-manualCh:
		if manualInput == "" {
			return nil, fmt.Errorf("manual input canceled")
		}
		// Extract code from URL if it's a full URL
		code := manualInput
		if strings.Contains(manualInput, "?") {
			u, err := url.Parse(manualInput)
			if err == nil {
				code = u.Query().Get("code")
			}
		}
		if code == "" {
			return nil, fmt.Errorf("could not find authorization code in input")
		}
		return ExchangeCodeForTokens(cfg, code, pkce.CodeVerifier, redirectURI)
	case <-time.After(5 * time.Minute):
		return nil, fmt.Errorf("authentication timed out after 5 minutes")
	}
}

func oauthCallbackRedirectURI(port int) string {
	return fmt.Sprintf("http://localhost:%d/auth/callback", port)
}

func oauthCallbackHandler(state string, resultCh chan<- callbackResult) http.Handler {
	mux := http.NewServeMux()
	mux.HandleFunc("/auth/callback", func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Query().Get("state") != state {
			resultCh <- callbackResult{err: fmt.Errorf("state mismatch")}
			http.Error(w, "State mismatch", http.StatusBadRequest)
			return
		}

		code := r.URL.Query().Get("code")
		if code == "" {

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Re-run the login command and complete it promptly — a fresh state/PKCE pair is generated each run
  2. If no browser opened, manually open the printed authorization URL immediately
  3. Open the auth URL on a phone/other machine right away so the redirect lands within the window
  4. For truly slow flows, prefer the device-code flow (RequestDeviceCode) which polls with the server-returned interval
Defensive patterns

Strategy: retry

Try / catch

cred, err := auth.LoginBrowserWithOptions(cfg, opts)
if err != nil && strings.Contains(err.Error(), "authentication timed out") {
    fmt.Println("5-minute window expired — restarting login; complete consent promptly")
    cred, err = auth.LoginBrowserWithOptions(cfg, opts) // fresh state/PKCE each run
}
if err != nil { return err }

Prevention

When it happens

Trigger: User starts login, gets distracted; browser fails to open (no xdg-open/open on the host) so the user never sees the auth page; user completes consent only after 5+ minutes of fiddling with login/2FA.

Common situations: Headless boxes missing a browser opener; slow SSO/MFA journeys; SSH sessions where the printed URL had to be carried to another machine and the round trip exceeded 5 minutes.

Understand the failure class

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/bc7558d1f887a645. Report an issue: GitHub.