{"record":{"id":"ad86a436a69a7651","repo":"chenhg5/cc-connect","slug":"invalid-json-ad86a4","errorCode":null,"errorMessage":"invalid JSON: ","messagePattern":"invalid JSON: ","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"core/webhook.go","lineNumber":99,"sourceCode":"\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}\n\tif req.Prompt == \"\" && req.Exec == \"\" {\n\t\thttp.Error(w, \"either prompt or exec is required\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tif req.Prompt != \"\" && req.Exec != \"\" {\n\t\thttp.Error(w, \"prompt and exec are mutually exclusive\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tengine, err := ws.resolveEngine(req.Project)\n\tif err != nil {","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/core/webhook.go#L81-L117","documentation":"The request body could not be decoded into the WebhookRequest struct. The handler returns 400 with \"invalid JSON: <detail>\". The webhook expects a JSON object with fields like session_key, prompt/exec, project, event.","triggerScenarios":"Sending empty body, malformed JSON (trailing commas, unquoted keys), wrong Content-Type payloads (form-encoded), or non-object JSON like arrays/strings.","commonSituations":"curl -d without quotes mangling the JSON in the shell; sender serializing with a non-JSON content type; truncated body from a proxy; template rendering producing empty body.","solutions":["Validate the JSON payload locally before sending (jq . or a linter).","Set Content-Type: application/json and send a JSON object body.","Check for shell quoting issues — wrap the -d payload in single quotes.","Read the appended err.Error() detail to pinpoint the syntax error position."],"exampleFix":"// before\n{\"session_key\": \"s1\", \"prompt\": \"hi\",}\n// after\n{\"session_key\": \"s1\", \"prompt\": \"hi\"}","handlingStrategy":"validation","validationCode":"payload, _ := json.Marshal(WebhookRequest{SessionKey: key, Prompt: p})\nvar check map[string]any\nif err := json.Unmarshal(payload, &check); err != nil {\n    return fmt.Errorf(\"payload is not valid JSON: %w\", err)\n}","typeGuard":"func isBadRequest(resp *http.Response) bool { return resp != nil && resp.StatusCode == http.StatusBadRequest }","tryCatchPattern":"resp, err := http.Post(url, \"application/json\", bytes.NewReader(payload))\nif err == nil && resp.StatusCode == 400 {\n    b, _ := io.ReadAll(resp.Body)\n    return fmt.Errorf(\"webhook rejected payload: %s\", b) // includes 'invalid JSON: ...' detail\n}","preventionTips":["Serialize payloads with a JSON library, never string concatenation","Run jq . over templated payloads in CI before shipping them","Quote curl -d bodies in single quotes to avoid shell mangling","Set Content-Type: application/json explicitly"],"tags":["json","webhook","validation","http-400"],"backgroundTag":"json-unmarshal-failed","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"}