sipeed/picoclaw · warning
Validation error: %v
Error message
Validation error: %v
What it means
HTTP 400 returned by POST /api/models (handleAddModel) when validateIncomingModelConfig rejects the normalized payload. Rejections come from ModelConfig.Validate (model_name required, model required, whitespace in model id, leading '/', consecutive '//', bad tool_schema_transform), a missing or unsupported provider, an ElevenLabs model other than the single supported id, or a provider not creatable right now (e.g. claude-cli/codex-cli when the CLI executable is not installed/authed).
Source
Thrown at web/backend/api/models.go:330
return
}
defer r.Body.Close()
type custom struct {
config.ModelConfig
APIKey string `json:"api_key"`
}
var mc custom
if err = json.Unmarshal(body, &mc); err != nil {
http.Error(w, fmt.Sprintf("Invalid JSON: %v", err), http.StatusBadRequest)
return
}
normalizeIncomingModelConfig(&mc.ModelConfig)
if err = validateIncomingModelConfig(&mc.ModelConfig, nil); err != nil {
http.Error(w, fmt.Sprintf("Validation error: %v", err), http.StatusBadRequest)
return
}
if mc.APIKey != "" {
mc.ModelConfig.SetAPIKey(mc.APIKey)
}
cfg, err := config.LoadConfig(h.configPath)
if err != nil {
http.Error(w, fmt.Sprintf("Failed to load config: %v", err), http.StatusInternalServerError)
return
}
cfg.ModelList = append(cfg.ModelList, &mc.ModelConfig)
normalizeStoredModelProviders(cfg)
if err := config.SaveConfig(h.configPath, cfg); err != nil {
http.Error(w, fmt.Sprintf("Failed to save config: %v", err), http.StatusInternalServerError)View on GitHub (pinned to 49183d7e8d)
Solutions
- Read the %v detail — it names the exact failing rule (e.g. 'model_name is required', 'provider "x" is not supported')
- Supply non-empty model_name and model, and pick provider from the provider_options array returned by GET /api/models
- Normalize the model id: trim whitespace, no leading '/', no '//' — the provider prefix should come from the provider field, not be glued twice
- For elevenlabs use the exact supported model id from the error message; for claude-cli/codex-cli install and log in to the CLI first, then retry
Example fix
// before: rejected — no model_name, provider typo, spaced model
{ "model": "openai/gpt 4o", "provider": "opennai" }
// after: accepted
{ "model_name": "gpt4o", "model": "gpt-4o", "provider": "openai", "api_base": "https://api.openai.com/v1", "api_key": "sk-..." } Defensive patterns
Strategy: validation
Validate before calling
function validateModelDraft(draft, providerOptions) {
if (!draft.model_name?.trim()) throw new Error('model_name is required');
if (!draft.model?.trim()) throw new Error('model is required');
if (/\s/.test(draft.model)) throw new Error('model id must not contain whitespace');
if (draft.model.startsWith('/') || draft.model.includes('//')) throw new Error('model id must not start with / or contain //');
if (!providerOptions.some(p => p.id === draft.provider)) throw new Error(`unsupported provider ${draft.provider}`);
} Type guard
function isCreatableModelDraft(draft, providerOptions) {
const opt = providerOptions.find(p => p.id === draft.provider);
return Boolean(opt) && opt.create_allowed === true
&& typeof draft.model_name === 'string' && draft.model_name.trim() !== ''
&& typeof draft.model === 'string' && /^[^\s/]/.test(draft.model) && !draft.model.includes('//');
} Try / catch
const res = await fetch('/api/models', { method: 'POST', body });
if (!res.ok && (await res.text()).startsWith('Validation error')) {
// show the rule detail to the user; fix fields and resubmit — do not blind-retry
} Prevention
- Populate the provider dropdown from provider_options in the GET /api/models response instead of free text
- Require model_name and model in the form before enabling submit
- Trim whitespace from model ids client-side; reject leading '/' and '//'
- For CLI providers, confirm the CLI is installed and authenticated before creating entries
When it happens
Trigger: POST /api/models with empty model_name or model; provider "foo" (not in providers.IsSupportedModelProvider); model "gpt-4 o" or "/gpt-4o" or "openai//gpt-4o"; provider elevenlabs with model "elevenlabs-turbo" instead of the supported id; provider claude-cli when the claude binary is missing so createAllowedForProvider is false.
Common situations: Form submitted with required fields blank; provider name typed freehand instead of picked from the provider_options list; users assuming any model id works for ElevenLabs; CLI providers (claude-cli, codex-cli) used before installing or authenticating the underlying CLI; copy-pasting model ids that contain spaces or leading slashes.
Related errors
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/8c750c4f41fd62e5.
Report an issue: GitHub.