{"record":{"id":"c74145687bb4543a","repo":"plandex-ai/plandex","slug":"error-decoding-request","errorCode":null,"errorMessage":"Error decoding request: ","messagePattern":"Error decoding request: ","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"app/server/handlers/plans_changes.go","lineNumber":324,"sourceCode":"\tif auth == nil {\n\t\treturn\n\t}\n\n\tvars := mux.Vars(r)\n\tplanId := vars[\"planId\"]\n\tbranch := vars[\"branch\"]\n\n\tlog.Println(\"planId: \", planId, \"branch: \", branch)\n\n\tif authorizePlan(w, planId, auth) == nil {\n\t\treturn\n\t}\n\n\tvar req shared.RejectFileRequest\n\terr := json.NewDecoder(r.Body).Decode(&req)\n\tif err != nil {\n\t\tlog.Printf(\"Error decoding request: %v\\n\", err)\n\t\thttp.Error(w, \"Error decoding request: \"+err.Error(), http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tctx, cancel := context.WithCancel(r.Context())\n\n\terr = db.ExecRepoOperation(db.ExecRepoOperationParams{\n\t\tOrgId:          auth.OrgId,\n\t\tUserId:         auth.User.Id,\n\t\tPlanId:         planId,\n\t\tBranch:         branch,\n\t\tScope:          db.LockScopeWrite,\n\t\tCtx:            ctx,\n\t\tCancelFn:       cancel,\n\t\tClearRepoOnErr: true,\n\t}, func(repo *db.GitRepo) error {\n\t\terr = db.RejectPlanFile(auth.OrgId, planId, req.FilePath, time.Now())\n\t\tif err != nil {\n\t\t\treturn err","sourceCodeStart":306,"sourceCodeEnd":342,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/handlers/plans_changes.go#L306-L342","documentation":"RejectFileHandler failed to decode the request body into shared.RejectFileRequest via json.NewDecoder(r.Body).Decode. The server returns HTTP 400 with this message because the JSON payload is malformed or of the wrong shape.","triggerScenarios":"POST to the reject-file endpoint with a non-JSON body, invalid JSON syntax (unquoted keys, trailing commas), wrong Content-Type body, an empty body, or a body whose fields don't match RejectFileRequest (e.g. filePath as a number instead of string).","commonSituations":"Client SDK sends form-encoded instead of JSON; curl command missing -d or using single quotes incorrectly on Windows; API version change renamed the field; a proxy strips the body on redirects.","solutions":["Inspect the err.Error() appended to the message — it pinpoints the JSON syntax offset or type mismatch","Log/verify the raw request body the client sent before decoding","Ensure the client sends Content-Type: application/json and a valid JSON object like {\"filePath\":\"...\"}","Check that RejectFileRequest field names/tags match what the client sends"],"exampleFix":"// before\ncurl -X POST .../plans/123/branch/main/reject -d 'filePath=foo.go'\n// after\ncurl -X POST .../plans/123/branch/main/reject -H 'Content-Type: application/json' -d '{\"filePath\":\"foo.go\"}'","handlingStrategy":"validation","validationCode":"const body = JSON.stringify({ filePath });\nJSON.parse(body); // throws early if payload is malformed\nconst res = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body });","typeGuard":"function isValidRejectFileRequest(v) { return typeof v === 'object' && v !== null && typeof v.filePath === 'string' && v.filePath.length > 0; }","tryCatchPattern":"try { const res = await rejectFile(planId, branch, filePath); } catch (e) { if (e.status === 400 && e.message.startsWith('Error decoding request')) { console.error('Invalid payload:', e.message); } else throw e; }","preventionTips":["Always send Content-Type: application/json","Validate payload shape client-side before the call","Log the exact body being sent when debugging 400s","Keep client types in sync with shared.RejectFileRequest"],"tags":["http","json","bad-request","go"],"backgroundTag":"json-decode-failed","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}