plandex-ai/plandex · error

'%s' is not a built-in model provider

Error message

'%s' is not a built-in model provider

What it means

A 422 validation error thrown when a custom model references provider.Provider as a non-custom value that is not a key in shared.BuiltInModelProviderConfigs. Only built-in provider names (and 'custom' with a valid custom provider) are accepted. This catches misspelled or entirely unknown provider identifiers.

Source

Thrown at app/server/handlers/models.go:210

	for _, model := range updatedModelsInput.CustomModels {
		// ensure that providers to upsert are either built-in, being imported, or already exist
		for _, provider := range model.Providers {
			if provider.Provider == shared.ModelProviderCustom {
				_, exists := existingCustomProviderNames[*provider.CustomProvider]
				_, creating := inputProviderNames[*provider.CustomProvider]
				if !exists && !creating {
					msg := fmt.Sprintf("'%s' is not a custom model provider that exists or is being imported", *provider.CustomProvider)
					log.Println(msg)
					http.Error(w, msg, http.StatusUnprocessableEntity)
					return
				}
			} else {
				pc, builtIn := shared.BuiltInModelProviderConfigs[provider.Provider]
				if !builtIn {
					msg := fmt.Sprintf("'%s' is not a built-in model provider", provider.Provider)
					log.Println(msg)
					http.Error(w, msg, http.StatusUnprocessableEntity)
					return
				}
				if os.Getenv("IS_CLOUD") != "" && pc.LocalOnly {
					msg := fmt.Sprintf("'%s' is a local-only model provider, so it can't be used on Plandex Cloud", provider.Provider)
					log.Println(msg)
					http.Error(w, msg, http.StatusUnprocessableEntity)
					return
				}
			}
		}

		dbModel := db.CustomModelFromApi(model)
		dbModel.Id = model.Id
		dbModel.OrgId = auth.OrgId

		toUpsertCustomModels = append(toUpsertCustomModels, dbModel)
	}

View on GitHub (pinned to e2d772072e)

Solutions

  1. Use an exact built-in provider name (e.g. openai, anthropic, azure) — check shared.BuiltInModelProviderConfigs for valid keys
  2. If the provider is truly custom, set provider to "custom" and supply a CustomProviders entry in the same request
  3. Trim whitespace and fix case in the provider field
  4. Upgrade server and client to matching versions so provider catalogs align

Example fix

// before
{"provider": "anthropicai"}
// after
{"provider": "anthropic"}
Defensive patterns

Strategy: validation

Validate before calling

// validate provider names against built-ins before calling the API
builtIn := map[string]bool{"openai": true, "anthropic": true, "azure": true /* ...shared.BuiltInModelProviderConfigs */}
for _, m := range in.CustomModels {
    for _, pr := range m.Providers {
        if pr.Provider != "custom" && !builtIn[pr.Provider] {
            return fmt.Errorf("unknown built-in provider: %q", pr.Provider)
        }
    }
}

Type guard

func isBuiltInProvider(p shared.ModelProvider) bool {
    _, ok := shared.BuiltInModelProviderConfigs[p]
    return ok
}

Try / catch

// deterministic 422 — fix input, never retry
if resp.StatusCode == 422 && strings.Contains(bodyText, "is not a built-in model provider") {
    var msg string
    json.Unmarshal(bodyBytes, &msg)
    return fmt.Errorf("fix provider name: %s", msg)
}

Prevention

When it happens

Trigger: POST to the upsert custom models endpoint with CustomModels[].Providers[].provider set to a string that isn't a built-in provider key — e.g. 'openai ' (trailing space), 'anthropicai', an old/renamed provider id, or a lowercased/uppercased variant.

Common situations: Hand-edited models.json config with a typo; copying a provider name from another tool that uses different ids; client/server version skew where the server is older than the provider name the client sends.

Related errors


AI-assisted analysis of plandex-ai/plandex@e2d772072e (2026-09-05). Data as JSON: /api/errors/fda90e24f69091b4. Report an issue: GitHub.