router-for-me/CLIProxyAPI · error

timeout waiting for OAuth callback

Error message

timeout waiting for OAuth callback

What it means

Thrown by WaitForCallback when the local OAuth callback HTTP server does not receive the provider redirect within the caller-supplied timeout. The function blocks on a select over the result channel, the error channel, and time.After(timeout); if the browser never hits the callback URL before the deadline, the timeout branch wins. It means the authorization leg of the flow (user login + redirect) never completed, not that the token exchange failed.

Source

Thrown at internal/auth/codex/oauth_server.go:154

// 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 the login flow and complete the browser prompt promptly, keeping the callback port reachable.
  2. Verify the callback port is free and matches what the flow registered: `lsof -i :<callback-port>` and re-run with `--oauth-callback-port <same-port>`.
  3. On SSH/containers, forward the callback port (e.g. `ssh -L 1455:127.0.0.1:1455`) or run the login on a machine with a browser.
  4. Disable proxies for 127.0.0.1/localhost (NO_PROXY) so the browser redirect is not swallowed.
  5. Increase the timeout value passed to WaitForCallback if the environment is known to be slow.

Example fix

// before
result, err := server.WaitForCallback(2 * time.Minute)

// after
result, err := server.WaitForCallback(5 * time.Minute)
if err != nil {
    log.Errorf("oauth callback not received: %v; check the callback port is reachable from your browser", err)
}
Defensive patterns

Strategy: retry

Validate before calling

// Before starting the flow, confirm the callback port is actually free
ln, err := net.Listen("tcp", ":1455")
if err != nil {
    log.Fatalf("callback port busy: %v", err)
}
_ = ln.Close() // OAuthServer will re-bind it

Try / catch

result, err := server.WaitForCallback(5 * time.Minute)
if err != nil {
    if strings.Contains(err.Error(), "timeout waiting for OAuth callback") {
        // restart the whole flow: shutdown server, regenerate PKCE, re-open browser
        log.Warnf("login timed out; restarting OAuth flow")
        continue // next loop iteration
    }
    return err
}

Prevention

When it happens

Trigger: User never opens or completes the login page; browser redirects to a different port than the one the OAuthServer listens on (--oauth-callback-port mismatch); the callback port is occupied by another process so the real server never gets the request; headless/SSH environment with no browser to reach 127.0.0.1; firewall or browser extension blocks the localhost redirect; timeout passed by the caller is too short for slow interactive login.

Common situations: Running cli-proxy-api login inside a container or over SSH without port forwarding; two login attempts racing for the same callback port; corporate proxy stripping the redirect; user walks away from the browser past the timeout (commonly 2-5 minutes).

Understand the failure class

Related errors


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