{"record":{"id":"249572a193a6dadd","repo":"plandex-ai/plandex","slug":"error-parsing-request-body-249572","errorCode":null,"errorMessage":"Error parsing request body","messagePattern":"Error parsing request body","errorType":"http","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"app/server/handlers/plans_crud.go","lineNumber":63,"sourceCode":"\t_, apiErr := hooks.ExecHook(hooks.WillCreatePlan, hooks.HookParams{Auth: auth})\n\tif apiErr != nil {\n\t\twriteApiError(w, *apiErr)\n\t\treturn\n\t}\n\n\t// read the request body\n\tbody, err := io.ReadAll(r.Body)\n\tif err != nil {\n\t\tlog.Printf(\"Error reading request body: %v\\n\", err)\n\t\thttp.Error(w, \"Error reading request body\", http.StatusInternalServerError)\n\t\treturn\n\t}\n\tdefer r.Body.Close()\n\n\tvar requestBody shared.CreatePlanRequest\n\tif err := json.Unmarshal(body, &requestBody); err != nil {\n\t\tlog.Printf(\"Error parsing request body: %v\\n\", err)\n\t\thttp.Error(w, \"Error parsing request body\", http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tname := requestBody.Name\n\tif name == \"\" {\n\t\tname = \"draft\"\n\t}\n\n\tif name == \"draft\" {\n\t\t// delete any existing draft plans\n\t\terr = db.DeleteDraftPlans(auth.OrgId, projectId, auth.User.Id)\n\n\t\tif err != nil {\n\t\t\tlog.Printf(\"Error deleting draft plans: %v\\n\", err)\n\t\t\thttp.Error(w, \"Error deleting draft plans: \"+err.Error(), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\t} else {","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/handlers/plans_crud.go#L45-L81","documentation":"CreatePlanHandler returns this 400 Bad Request when json.Unmarshal fails to parse the request body into shared.CreatePlanRequest. The body arrived but is not valid JSON, is empty, or has fields whose types do not match the struct (e.g. name as a number). This is a client-side payload problem by design.","triggerScenarios":"POST create-plan with an empty body, invalid JSON syntax (trailing commas, unquoted keys), wrong Content-Type leading to form-encoded data being sent, or a JSON body where \"name\" is not a string.","commonSituations":"Automation scripts sending form data instead of JSON; missing Content-Type header plus non-JSON body; hand-written curl with quoting/escaping mistakes; API version drift where clients send a schema that no longer matches CreatePlanRequest.","solutions":["Validate the request body is well-formed JSON matching CreatePlanRequest ({\"name\":\"...\"})","Send header Content-Type: application/json","Check that the name field is a JSON string, not a number or object","Test the exact payload with a JSON linter or jq before sending","Confirm client SDK/API version matches the server's expected CreatePlanRequest schema"],"exampleFix":"// before\ncurl -X POST $URL/plans -d name=myplan\n// after\ncurl -X POST $URL/plans -H 'Content-Type: application/json' -d '{\"name\":\"myplan\"}'","handlingStrategy":"validation","validationCode":"function validateCreatePlanRequest(body) {\n  const parsed = JSON.parse(body); // throws SyntaxError on invalid JSON before hitting the API\n  if (parsed.name !== undefined && typeof parsed.name !== 'string') {\n    throw new Error('name must be a string');\n  }\n  return parsed;\n}","typeGuard":"function isCreatePlanRequest(v) {\n  return typeof v === 'object' && v !== null\n    && (v.name === undefined || typeof v.name === 'string');\n}","tryCatchPattern":"try {\n  await createPlan(projectId, name);\n} catch (e) {\n  if (/Error parsing request body/.test(e.message)) {\n    console.error('Invalid JSON payload sent to create-plan:', {name});\n  }\n  throw e;\n}","preventionTips":["Always send Content-Type: application/json","Pre-validate payloads with JSON.parse/jq before sending","Keep client payload schemas in sync with shared.CreatePlanRequest","Test API calls with curl examples from current docs"],"tags":["http-400","json","validation","client-error"],"backgroundTag":"invalid-json-body","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"}