{"record":{"id":"69cc59faa744e9ef","repo":"router-for-me/CLIProxyAPI","slug":"oauth-error-s","errorCode":null,"errorMessage":"OAuth error: %s","messagePattern":"OAuth error: (.+?)","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"internal/auth/claude/oauth_server.go","lineNumber":190,"sourceCode":"\tif r.Method != http.MethodGet {\n\t\thttp.Error(w, \"Method not allowed\", http.StatusMethodNotAllowed)\n\t\treturn\n\t}\n\n\t// Extract parameters\n\tquery := r.URL.Query()\n\tcode := query.Get(\"code\")\n\tstate := query.Get(\"state\")\n\terrorParam := query.Get(\"error\")\n\n\t// Validate required parameters\n\tif errorParam != \"\" {\n\t\tlog.Errorf(\"OAuth error received: %s\", errorParam)\n\t\tresult := &OAuthResult{\n\t\t\tError: errorParam,\n\t\t}\n\t\ts.sendResult(result)\n\t\thttp.Error(w, fmt.Sprintf(\"OAuth error: %s\", errorParam), http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tif code == \"\" {\n\t\tlog.Error(\"No authorization code received\")\n\t\tresult := &OAuthResult{\n\t\t\tError: \"no_code\",\n\t\t}\n\t\ts.sendResult(result)\n\t\thttp.Error(w, \"No authorization code received\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tif state == \"\" {\n\t\tlog.Error(\"No state parameter received\")\n\t\tresult := &OAuthResult{\n\t\t\tError: \"no_state\",\n\t\t}","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/auth/claude/oauth_server.go#L172-L208","documentation":"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>\".","triggerScenarios":"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.","commonSituations":"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.","solutions":["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","Re-run the login flow (the handler has already delivered the error to the waiting goroutine, which surfaces it to the CLI)","Check the provider's app console for the client's status and redirect URI configuration"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// nothing to pre-validate on the caller side; the error originates upstream.\n// After the flow: inspect the returned OAuthResult\nresult, err := server.WaitForCode(ctx)\nif err == nil && result.Error != \"\" { switch result.Error {\n    case \"access_denied\": /* user declined; prompt and retry login */\n    case \"invalid_scope\": /* request fewer scopes, retry */\n    default: /* surface result.Error to the user */\n} }","typeGuard":"func oauthFailed(r *OAuthResult) bool { return r != nil && r.Error != \"\" }","tryCatchPattern":"result, err := server.WaitForCode(ctx)\nif err != nil { return fmt.Errorf(\"oauth wait: %w\", err) }\nif result.Error != \"\" { return fmt.Errorf(\"oauth provider error: %s\", result.Error) }","preventionTips":["Pre-verify client_id/redirect_uri/scopes in the provider console","Educate users that cancelling consent surfaces access_denied","Handle result.Error explicitly in every login wrapper"],"tags":["claude","oauth","authorization","callback"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}