{"record":{"id":"97ae51d0717e2d6e","repo":"plandex-ai/plandex","slug":"model-id-is-required","errorCode":null,"errorMessage":"Model id is required","messagePattern":"Model id is required","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"app/server/handlers/models.go","lineNumber":80,"sourceCode":"\tif !hasDuplicates {\n\t\thttp.Error(w, \"Has duplicates: \"+errMsg, http.StatusBadRequest)\n\t\treturn\n\t}\n\n\tfor _, provider := range modelsInput.CustomProviders {\n\t\tif provider.Name == \"\" {\n\t\t\tmsg := \"Provider name is required\"\n\t\t\tlog.Println(msg)\n\t\t\thttp.Error(w, msg, http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\t}\n\n\tfor _, model := range modelsInput.CustomModels {\n\t\tif model.ModelId == \"\" {\n\t\t\tmsg := \"Model id is required\"\n\t\t\tlog.Println(msg)\n\t\t\thttp.Error(w, msg, http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\n\t\tif shared.BuiltInBaseModelsById[model.ModelId] != nil {\n\t\t\tmsg := fmt.Sprintf(\"%s is a built-in base model id, so it can't be used for a custom model\", model.ModelId)\n\t\t\tlog.Println(msg)\n\t\t\thttp.Error(w, msg, http.StatusUnprocessableEntity)\n\t\t\treturn\n\t\t}\n\t}\n\n\tfor _, modelPack := range modelsInput.CustomModelPacks {\n\t\tif modelPack.Name == \"\" {\n\t\t\tmsg := \"Model pack name is required\"\n\t\t\tlog.Println(msg)\n\t\t\thttp.Error(w, msg, http.StatusBadRequest)\n\t\t\treturn\n\t\t}","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/handlers/models.go#L62-L98","documentation":"Request validation in UpsertCustomModelsHandler: a custom model in the payload has an empty ModelId. Pure input validation — the model cannot be referenced without an id, so the request is rejected with 400 before any writes.","triggerScenarios":"POSTing to the custom models upsert endpoint with a CustomModels element whose modelId field is missing or empty.","commonSituations":"Hand-editing a models.json and dropping the modelId line; generating models from a loop where the id variable is empty; copy-paste of a model template where only baseUrl/apiKey were filled in.","solutions":["Set a unique non-empty modelId on every object in the customModels array","Validate model ids client-side before sending the request","Check the request JSON for entries where modelId is \"\" or absent","Avoid model ids that collide with built-in base model ids (that raises a separate 422 error)"],"exampleFix":"// before\n{\"customModels\": [{\"provider\": \"openai\", \"baseUrl\": \"...\"}]}\n// after\n{\"customModels\": [{\"modelId\": \"my-model\", \"provider\": \"openai\", \"baseUrl\": \"...\"}]}","handlingStrategy":"validation","validationCode":"for i, m := range input.CustomModels {\n    if m.ModelId == \"\" {\n        return fmt.Errorf(\"customModels[%d].modelId is required\", i)\n    }\n}","typeGuard":"func modelIdentified(m *shared.CustomModel) bool { return m != nil && m.ModelId != \"\" }","tryCatchPattern":null,"preventionTips":["Validate every custom model has a non-empty modelId before submitting","Also check the id is not a built-in base model id","Make modelId a required field in any config generator"],"tags":["http","go","validation","missing-field","bad-request"],"backgroundTag":"missing-required-argument","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}