router-for-me/CLIProxyAPI · error · AuthenticationError

callback_timeout

callback_timeout

Error message

timeout waiting for OAuth callback

What it means

WaitForCallback waited the full timeout without receiving the browser redirect on resultChan and without a server error. It carries code=callback_timeout: the login server was up, but the authorization code never arrived in time. Almost always the human/browser side of the flow, not the network stack.

Source

Thrown at internal/auth/claude/oauth_server.go:157

// WaitForCallback waits for the OAuth callback with a timeout.
// It blocks until either an OAuth result is received, an error occurs,
// or the specified timeout is reached.
//
// Parameters:
//   - timeout: The maximum time to wait for the callback
//
// Returns:
//   - *OAuthResult: The OAuth result if successful
//   - error: An error if the callback times out or an error occurs
func (s *OAuthServer) WaitForCallback(timeout time.Duration) (*OAuthResult, error) {
	select {
	case result := <-s.resultChan:
		return result, nil
	case err := <-s.errorChan:
		return nil, err
	case <-time.After(timeout):
		return nil, fmt.Errorf("timeout waiting for OAuth callback")
	}
}

// handleCallback handles the OAuth callback endpoint.
// It extracts the authorization code and state from the callback URL,
// validates the parameters, and sends the result to the waiting channel.
//
// Parameters:
//   - w: The HTTP response writer
//   - r: The HTTP request
func (s *OAuthServer) handleCallback(w http.ResponseWriter, r *http.Request) {
	log.Debug("Received OAuth callback")

	// Validate request method
	if r.Method != http.MethodGet {
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Re-run login and complete the browser step promptly; if on a remote host, forward the callback port (ssh -L <port>:127.0.0.1:<port>) so the redirect reaches the server.
  2. Verify the redirect URI/port in the auth URL matches the server's actual port (check --oauth-callback-port).
  3. Increase the callback wait timeout if your SSO/MFA flow is slow.
  4. Ensure nothing (firewall, browser policy) blocks the browser from reaching http://127.0.0.1:<port>/callback.

Example fix

# before (remote box, no forwarding — browser redirect never arrives)
ssh user@server
./cli-proxy-api login

# after
ssh -L 53100:127.0.0.1:53100 user@server
./cli-proxy-api login --oauth-callback-port 53100
Defensive patterns

Strategy: retry

Validate before calling

if runtime.GOOS == "linux" && isRemoteSession() {
    fmt.Println("Forward the callback port before continuing:", port)
}

Try / catch

res, err := server.WaitForCallback(5 * time.Minute)
if err != nil && strings.Contains(err.Error(), "timeout waiting") {
    res, err = restartFlowWithLongerTimeout(server, 10*time.Minute)
}

Prevention

When it happens

Trigger: User opened the consent page but never clicked approve; browser redirected to a different host/port than the callback server's (wrong --oauth-callback-port or redirect URI); user is on a remote/HEADLESS machine and the opened browser is not reachable from the server's host; the default timeout elapsed before the user completed login.

Common situations: Headless servers where the auth URL is printed but the callback would hit the wrong machine; SSH sessions without port forwarding; corporate browsers blocking localhost redirects; slow SSO/MFA flows exceeding the default wait; mistyped callback port so the browser hits a dead port.

Understand the failure class

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/93d479b0f39435d4. Report an issue: GitHub.