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

  1. Read the %v detail — it names the exact failing rule (e.g. 'model_name is required', 'provider "x" is not supported')
  2. Supply non-empty model_name and model, and pick provider from the provider_options array returned by GET /api/models
  3. Normalize the model id: trim whitespace, no leading '/', no '//' — the provider prefix should come from the provider field, not be glued twice
  4. 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

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.