router-for-me/CLIProxyAPI · error

OAuth error: %s

Error message

OAuth error: %s

What it means

The Claude OAuth callback received an error query parameter — the authorization server refused the request and redirected back with ?error=... (per OAuth2 RFC 6749 §4.1.2.1). The handler logs it, sends OAuthResult{Error: errorParam} to the waiting flow, and returns HTTP 400 with body "OAuth error: <errorParam>".

Source

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

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

	// Extract parameters
	query := r.URL.Query()
	code := query.Get("code")
	state := query.Get("state")
	errorParam := query.Get("error")

	// Validate required parameters
	if errorParam != "" {
		log.Errorf("OAuth error received: %s", errorParam)
		result := &OAuthResult{
			Error: errorParam,
		}
		s.sendResult(result)
		http.Error(w, fmt.Sprintf("OAuth error: %s", errorParam), http.StatusBadRequest)
		return
	}

	if code == "" {
		log.Error("No authorization code received")
		result := &OAuthResult{
			Error: "no_code",
		}
		s.sendResult(result)
		http.Error(w, "No authorization code received", http.StatusBadRequest)
		return
	}

	if state == "" {
		log.Error("No state parameter received")
		result := &OAuthResult{
			Error: "no_state",
		}

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Read the error value: access_denied means the user declined — re-run login and approve; invalid_scope means trim/fix requested scopes; invalid_client means fix the client registration
  2. Re-run the login flow (the handler has already delivered the error to the waiting goroutine, which surfaces it to the CLI)
  3. Check the provider's app console for the client's status and redirect URI configuration
Defensive patterns

Strategy: fallback

Validate before calling

// nothing to pre-validate on the caller side; the error originates upstream.
// After the flow: inspect the returned OAuthResult
result, err := server.WaitForCode(ctx)
if err == nil && result.Error != "" { switch result.Error {
    case "access_denied": /* user declined; prompt and retry login */
    case "invalid_scope": /* request fewer scopes, retry */
    default: /* surface result.Error to the user */
} }

Type guard

func oauthFailed(r *OAuthResult) bool { return r != nil && r.Error != "" }

Try / catch

result, err := server.WaitForCode(ctx)
if err != nil { return fmt.Errorf("oauth wait: %w", err) }
if result.Error != "" { return fmt.Errorf("oauth provider error: %s", result.Error) }

Prevention

When it happens

Trigger: Browser lands on /callback?error=access_denied (user clicked deny), ?error=invalid_client, ?error=invalid_scope, or any provider-specific error code instead of a code parameter.

Common situations: User cancels the consent screen; requested scopes not granted to the CLI client; client_id misregistered; the provider revoked or expired the app's access during device authorization.

Related errors


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