plandex-ai/plandex · error
%s is a built-in base model id, so it can't be used for a cu
Error message
%s is a built-in base model id, so it can't be used for a custom model
What it means
Custom model ids must not shadow built-in base model ids. The handler checks each custom model's ModelId against shared.BuiltInBaseModelsById; a collision is rejected with HTTP 422 (Unprocessable Entity) and a message naming the offending id. This protects role resolution, which looks ids up in the built-in table first.
Source
Thrown at app/server/handlers/models.go:87
msg := "Provider name is required"
log.Println(msg)
http.Error(w, msg, http.StatusBadRequest)
return
}
}
for _, model := range modelsInput.CustomModels {
if model.ModelId == "" {
msg := "Model id is required"
log.Println(msg)
http.Error(w, msg, http.StatusBadRequest)
return
}
if shared.BuiltInBaseModelsById[model.ModelId] != nil {
msg := fmt.Sprintf("%s is a built-in base model id, so it can't be used for a custom model", model.ModelId)
log.Println(msg)
http.Error(w, msg, http.StatusUnprocessableEntity)
return
}
}
for _, modelPack := range modelsInput.CustomModelPacks {
if modelPack.Name == "" {
msg := "Model pack name is required"
log.Println(msg)
http.Error(w, msg, http.StatusBadRequest)
return
}
if shared.BuiltInModelPacksByName[modelPack.Name] != nil {
msg := fmt.Sprintf("%s is a built-in model pack name, so it can't be used for a custom model pack", modelPack.Name)
log.Println(msg)
http.Error(w, msg, http.StatusUnprocessableEntity)
return
}View on GitHub (pinned to e2d772072e)
Solutions
- Rename the custom model to a distinct id that does not collide with built-in base model ids
- If you want the built-in model's behavior, reference the built-in id directly in your model pack instead of redefining it
- To change defaults, define a custom model with a new id and assign it to the desired roles
- After a Plandex upgrade, re-check custom ids against the new BuiltInBaseModelsById list
Example fix
// before
{"customModels": [{"modelId": "gpt-4o", "baseUrl": "https://proxy.example.com"}]}
// after
{"customModels": [{"modelId": "gpt-4o-proxy", "baseUrl": "https://proxy.example.com"}]} Defensive patterns
Strategy: validation
Validate before calling
for _, m := range input.CustomModels {
if shared.BuiltInBaseModelsById[m.ModelId] != nil {
return fmt.Errorf("%s is a built-in base model id; choose a different custom id", m.ModelId)
}
} Type guard
func isBuiltInBaseModelId(id shared.ModelId) bool { return shared.BuiltInBaseModelsById[id] != nil } Prevention
- Check custom model ids against shared.BuiltInBaseModelsById before submitting
- Use suffixed ids (e.g. '-custom', '-proxy') for wrappers around built-in models
- Re-validate ids after every Plandex upgrade since built-ins can change
When it happens
Trigger: POSTing custom models where a modelId exactly matches a built-in base model id (e.g. "gpt-4o", "claude-3-5-sonnet-latest"), or renaming a custom model to a built-in id.
Common situations: Trying to override a built-in model's settings by reusing its id instead of creating a new id; a typo where a wrapper id was meant (e.g. "gpt-4o-custom"); upgrading Plandex and a previously-legal custom id becoming a new built-in id.
Related errors
- %s is a built-in model pack name, so it can't be used for a
- File %s is too large: %d (max %d)
- Has duplicates:
- Provider name is required
- Model id is required
AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05).
Data as JSON: /api/errors/287da3567619b256.
Report an issue: GitHub.