{"record":{"id":"3a4630a41464c476","repo":"chenhg5/cc-connect","slug":"post-only-3a4630","errorCode":null,"errorMessage":"POST only","messagePattern":"POST only","errorType":"http","errorClass":null,"httpStatus":405,"severity":"error","filePath":"core/webhook.go","lineNumber":88,"sourceCode":"\tgo func() {\n\t\tslog.Info(\"webhook: server started\", \"addr\", addr, \"path\", ws.path)\n\t\tif err := ws.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {\n\t\t\tslog.Error(\"webhook: server error\", \"error\", err)\n\t\t}\n\t}()\n}\n\nfunc (ws *WebhookServer) Stop() {\n\tif ws.server != nil {\n\t\tctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\t\tdefer cancel()\n\t\t_ = ws.server.Shutdown(ctx)\n\t}\n}\n\nfunc (ws *WebhookServer) handleHook(w http.ResponseWriter, r *http.Request) {\n\tif r.Method != http.MethodPost {\n\t\thttp.Error(w, \"POST only\", http.StatusMethodNotAllowed)\n\t\treturn\n\t}\n\n\tif !ws.authenticate(r) {\n\t\thttp.Error(w, \"unauthorized\", http.StatusUnauthorized)\n\t\treturn\n\t}\n\n\tvar req WebhookRequest\n\tif err := json.NewDecoder(r.Body).Decode(&req); err != nil {\n\t\thttp.Error(w, \"invalid JSON: \"+err.Error(), http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tif req.SessionKey == \"\" {\n\t\thttp.Error(w, \"session_key is required\", http.StatusBadRequest)\n\t\treturn\n\t}","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/webhook.go#L70-L106","documentation":"The webhook endpoint only accepts HTTP POST. Any request with a different method (GET, PUT, DELETE, etc.) is rejected with 405 and body \"POST only\" before authentication is even attempted. This enforces the webhook contract, since payloads are carried in the POST body.","triggerScenarios":"Sending GET/PUT/DELETE/OPTIONS to the WebhookServer's hook path — e.g. opening the URL in a browser (GET), a health-check prober hitting it with GET, or an API client defaulting to PUT.","commonSituations":"Verifying the webhook URL by pasting it in a browser; uptime monitors configured for GET; CORS preflight (OPTIONS) reaching the handler; REST clients set to PUT.","solutions":["Change the request method to POST.","For health checks, configure the monitor to expect 405 or use a dedicated health endpoint.","If the sender is a framework defaulting to another method, explicitly set method: 'POST'."],"exampleFix":"// before\nfetch(url)\n// after\nfetch(url, { method: \"POST\", headers: {\"Authorization\": \"Bearer <token>\", \"Content-Type\": \"application/json\"}, body: JSON.stringify({session_key: \"s1\", prompt: \"hi\"}) })","handlingStrategy":"validation","validationCode":"if (typeof req.method === 'string' && req.method.toUpperCase() !== 'POST') {\n  throw new Error('webhook requires POST, got ' + req.method);\n}","typeGuard":"func isMethodNotAllowed(resp *http.Response) bool { return resp != nil && resp.StatusCode == http.StatusMethodNotAllowed }","tryCatchPattern":"resp, err := http.Post(url, \"application/json\", body)\nif err == nil && resp.StatusCode == 405 {\n    return errors.New(\"webhook endpoint only accepts POST — fix request method\")\n}","preventionTips":["Always construct webhook calls with http.Post / fetch method:'POST'","Never 'test' the webhook URL in a browser (that sends GET)","Configure monitors to hit a separate health endpoint","Add a client-side assertion that the method is POST before dispatch"],"tags":["http","webhook","method-not-allowed","http-405"],"backgroundTag":"http-error-response","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}