{"record":{"id":"e5c4043168a6463f","repo":"plandex-ai/plandex","slug":"invalid-request-body","errorCode":null,"errorMessage":"Invalid request body: ","messagePattern":"Invalid request body: ","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"app/server/handlers/models.go","lineNumber":34,"sourceCode":"\nconst CustomModelsMinClientVersion = \"2.2.0\"\n\nfunc UpsertCustomModelsHandler(w http.ResponseWriter, r *http.Request) {\n\tlog.Println(\"Received request for CreateCustomModelHandler\")\n\n\tauth := Authenticate(w, r, true)\n\tif auth == nil {\n\t\treturn\n\t}\n\n\tif !requireMinClientVersion(w, r, CustomModelsMinClientVersion) {\n\t\treturn\n\t}\n\n\tvar modelsInput shared.ModelsInput\n\tif err := json.NewDecoder(r.Body).Decode(&modelsInput); err != nil {\n\t\tlog.Printf(\"Error decoding request body: %v\\n\", err)\n\t\thttp.Error(w, \"Invalid request body: \"+err.Error(), http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tif len(modelsInput.CustomProviders) > 0 {\n\t\tif os.Getenv(\"IS_CLOUD\") != \"\" {\n\t\t\thttp.Error(w, \"Custom model providers are not supported on Plandex Cloud\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\t}\n\n\tif len(modelsInput.CustomModels) > 0 {\n\t\tif os.Getenv(\"IS_CLOUD\") != \"\" {\n\t\t\tapiOrg, err := getApiOrg(auth.OrgId)\n\t\t\tif err != nil {\n\t\t\t\tlog.Printf(\"Error fetching org: %v\\n\", err)\n\t\t\t\thttp.Error(w, \"Failed to create custom model: \"+err.Error(), http.StatusInternalServerError)\n\t\t\t\treturn\n\t\t\t}","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/handlers/models.go#L16-L52","documentation":"UpsertCustomModelsHandler decodes the request body into shared.ModelsInput with json.NewDecoder(r.Body).Decode. Any malformed body — invalid JSON, wrong types (e.g. string where an object/array is expected), trailing garbage, or an empty body — produces 400 'Invalid request body: <err>'. Unlike the invites marshalling errors, this one is caused by the client's payload.","triggerScenarios":"POST to the custom models upsert endpoint with: non-JSON body, truncated JSON, wrong field types (e.g. customModels as a string instead of array), Content-Type/body mismatch, or an empty body.","commonSituations":"Older CLI (<2.2.0) sending a legacy payload shape; hand-built curl missing quotes or with a stale JSON template; proxy stripping the body; client sending form-encoded data instead of JSON.","solutions":["Read the json error in the message/log (e.g. 'unexpected end of JSON input', 'cannot unmarshal string into Go value of type ...') to pinpoint the malformation","Send a valid JSON body matching shared.ModelsInput (customModels and customProviders arrays of objects)","Ensure the client version is >= 2.2.0 (CustomModelsMinClientVersion) and Content-Type is application/json"],"exampleFix":"// before\ncurl -X POST $BASE/custom-models -d 'customModels=my-model'\n// after\ncurl -X POST $BASE/custom-models -H 'Content-Type: application/json' \\\n  -d '{\"customModels\":[{\"provider\":\"my-provider\",\"model\":\"my-model\"}]}'","handlingStrategy":"validation","validationCode":"const body = { customModels: [{ provider: 'p', model: 'm' }], customProviders: [] }\nconst payload = JSON.stringify(body)\nJSON.parse(payload) // verify it's valid JSON before sending\nif (!Array.isArray(body.customModels)) throw new TypeError('customModels must be an array')\n// also ensure client version >= 2.2.0","typeGuard":"function isModelsInput(v) {\n\treturn v != null && typeof v === 'object' &&\n\t\tArray.isArray(v.customModels) && Array.isArray(v.customProviders)\n}","tryCatchPattern":"try {\n\tawait client.upsertCustomModels(body)\n} catch (e) {\n\tif (e.status === 400 && String(e.message).includes('Invalid request body')) {\n\t\tconsole.error('Payload is not valid JSON for ModelsInput:', e.message)\n\t}\n\tthrow e\n}","preventionTips":["Always JSON.stringify the payload and set Content-Type: application/json","Validate body against the ModelsInput schema before sending","Ensure client version meets the 2.2.0 minimum for custom models","Never send an empty body or form-encoded data to this endpoint"],"tags":["http-400","json","request-validation"],"backgroundTag":"invalid-json-request-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"}