{"record":{"id":"e6ccc5012d50aae6","repo":"router-for-me/CLIProxyAPI","slug":"method-not-allowed-e6ccc5","errorCode":null,"errorMessage":"Method not allowed","messagePattern":"Method not allowed","errorType":"http","errorClass":null,"httpStatus":405,"severity":"warning","filePath":"internal/auth/codex/oauth_server.go","lineNumber":170,"sourceCode":"\t\treturn nil, err\n\tcase <-time.After(timeout):\n\t\treturn nil, fmt.Errorf(\"timeout waiting for OAuth callback\")\n\t}\n}\n\n// handleCallback handles the OAuth callback endpoint.\n// It extracts the authorization code and state from the callback URL,\n// validates the parameters, and sends the result to the waiting channel.\n//\n// Parameters:\n//   - w: The HTTP response writer\n//   - r: The HTTP request\nfunc (s *OAuthServer) handleCallback(w http.ResponseWriter, r *http.Request) {\n\tlog.Debug(\"Received OAuth callback\")\n\n\t// Validate request method\n\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","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/auth/codex/oauth_server.go#L152-L188","documentation":"The Codex OAuth callback HTTP handler only accepts GET. Any other method (POST, HEAD, PUT, DELETE) receives a 405 with body \"Method not allowed\" and no OAuthResult is delivered to the waiting code exchange.","triggerScenarios":"POST to the Codex callback route (e.g. response_mode=form_post from the IdP), a HEAD health-check probe, or scripted tests using the wrong verb.","commonSituations":"Identity providers converting redirects into form posts; monitoring probes hitting the loopback callback port during login; curl invocations with -X POST copied from other examples.","solutions":["Ensure the callback arrives as a browser GET redirect (default query response mode)","Test with plain curl (GET): curl \"http://127.0.0.1:PORT/callback?code=x&state=y\"","Disable form_post response mode on the IdP for this client"],"exampleFix":"# before\ncurl -X POST \"http://127.0.0.1:1455/auth/callback?code=abc&state=xyz\"\n# 405 Method not allowed\n\n# after\ncurl \"http://127.0.0.1:1455/auth/callback?code=abc&state=xyz\"","handlingStrategy":"validation","validationCode":"if r.Method != http.MethodGet {\n    w.Header().Set(\"Allow\", http.MethodGet)\n    http.Error(w, \"GET only\", http.StatusMethodNotAllowed)\n    return\n}\n// when probing the Codex callback manually, always use GET","typeGuard":"func isGet(r *http.Request) bool { return r.Method == http.MethodGet }","tryCatchPattern":"result, err := server.WaitForCode(ctx)\nif err != nil || result.Error != \"\" { log.Printf(\"codex oauth failed: %v %s\", err, result.Error) }","preventionTips":["Disable form_post response mode for the Codex client","Point health checks away from the OAuth callback route"],"tags":["codex","oauth","http-405","callback"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}