sipeed/picoclaw · error

provider is required

Error message

provider is required

What it means

Returned by POST /api/models/fetch when the parsed JSON has an empty (or missing) provider field. Provider determines which upstream model-list API to call, so an empty value is rejected with 400 before any key/base fallback logic runs.

Source

Thrown at web/backend/api/models.go:654

	if err != nil {
		http.Error(w, "Failed to read request body", http.StatusBadRequest)
		return
	}
	defer r.Body.Close()

	var req struct {
		Provider   string `json:"provider"`
		APIKey     string `json:"api_key"`
		APIBase    string `json:"api_base"`
		ModelIndex *int   `json:"model_index,omitempty"`
	}
	if err = json.Unmarshal(body, &req); err != nil {
		http.Error(w, fmt.Sprintf("Invalid JSON: %v", err), http.StatusBadRequest)
		return
	}

	if req.Provider == "" {
		http.Error(w, "provider is required", http.StatusBadRequest)
		return
	}

	if !providers.IsModelProviderFetchable(req.Provider) {
		http.Error(w, fmt.Sprintf("provider %q does not support model listing", req.Provider), http.StatusBadRequest)
		return
	}

	apiKey := strings.TrimSpace(req.APIKey)
	apiBase := strings.TrimSpace(req.APIBase)

	if apiKey == "" && req.ModelIndex != nil {
		if stored := h.lookupStoredAPIKey(*req.ModelIndex, req.Provider, apiBase); stored != "" {
			apiKey = stored
		}
	}

	if apiBase == "" {

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Include a non-empty provider, e.g. {"provider": "openai", ...}
  2. Disable the submit button until the provider selector has a concrete value
  3. Fetch the provider list from the API (e.g. provider options endpoint) and use its exact IDs

Example fix

// before
body: JSON.stringify({ provider: providerId ?? '', api_key: key })

// after
if (!providerId) throw new Error('select a provider first');
body: JSON.stringify({ provider: providerId, api_key: key })
Defensive patterns

Strategy: validation

Validate before calling

function providerPayload(provider: string | undefined | null): { provider: string } {
  const p = (provider ?? '').trim();
  if (!p) throw new Error('provider is required');
  return { provider: p };
}

Type guard

function hasProvider(v: unknown): v is { provider: string } {
  return typeof v === 'object' && v !== null && typeof (v as any).provider === 'string' && (v as any).provider.trim() !== '';
}

Try / catch

try {
  const res = await fetch('/api/models/fetch', {...});
  if (res.status === 400 && (await res.text()) === 'provider is required') {
    /* keep the Fetch button disabled until a provider is selected */
  }
} catch (e) { /* network */ }

Prevention

When it happens

Trigger: POST /api/models/fetch with {}, {"provider": ""}, or {"api_key": "sk-..."} only. Also when the client sends a differently-named field (e.g. "provider_id") so Provider stays empty after unmarshal.

Common situations: Provider dropdown left on a placeholder option with value ""; state variable initialized as empty string and submitted before selection; field renamed on the frontend but not the backend.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/8e86a8d080e955a9. Report an issue: GitHub.