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
- Include a non-empty provider, e.g. {"provider": "openai", ...}
- Disable the submit button until the provider selector has a concrete value
- 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
- Use a placeholder-free select or disable submit until provider is chosen
- Populate provider options from the backend so ids are always valid and non-empty
- Trim and assert provider before serializing
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
- model_name is required
- Model %q cannot be used as the default chat model
- provider %q does not support model listing
- No default API base for provider %q
- Invalid JSON
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/8e86a8d080e955a9.
Report an issue: GitHub.