sipeed/picoclaw · error
No default API base for provider %q
Error message
No default API base for provider %q
What it means
Returned by POST /api/models/fetch when api_base was not supplied AND providers.DefaultAPIBaseForProtocol(provider) returns "" for that provider. The handler refuses to guess an endpoint: without an api_base it cannot build the upstream /models URL, so it returns 400 naming the provider.
Source
Thrown at web/backend/api/models.go:676
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 == "" {
apiBase = providers.DefaultAPIBaseForProtocol(req.Provider)
}
if apiBase == "" {
http.Error(w, fmt.Sprintf("No default API base for provider %q", req.Provider), http.StatusBadRequest)
return
}
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()
models, err := fetchUpstreamModels(ctx, req.Provider, apiBase, apiKey)
if err != nil {
http.Error(w, fmt.Sprintf("Failed to fetch models: %v", err), http.StatusBadGateway)
return
}
// Auto-save fetched models to catalog
catalogModels := make([]CatalogModel, len(models))
for i, m := range models {
catalogModels[i] = CatalogModel{ID: m.ID, OwnedBy: m.OwnedBy}
}
if saveErr := SaveCatalog(req.Provider, apiBase, apiKey, catalogModels); saveErr != nil {View on GitHub (pinned to 49183d7e8d)
Solutions
- Send an explicit api_base: {"provider": "...", "api_base": "http://localhost:11434/v1"}
- Verify the api_base value is non-empty after trimming — whitespace-only fails
- Confirm the provider id is the one that actually has a default base (e.g. plain "openai"), not a custom alias
Example fix
// before
body: JSON.stringify({ provider: 'custom', api_key: key })
// after
body: JSON.stringify({ provider: 'custom', api_base: 'http://localhost:11434/v1', api_key: key }) Defensive patterns
Strategy: validation
Validate before calling
function effectiveApiBase(provider: string, apiBase: string | undefined, defaults: Record<string,string>): string {
const base = (apiBase ?? '').trim();
if (base) return base;
const def = defaults[provider];
if (!def) throw new Error(`no default API base for ${provider} — api_base is required`);
return def;
} Type guard
function hasApiBase(v: unknown): v is { api_base: string } {
return typeof v === 'object' && v !== null && typeof (v as any).api_base === 'string' && (v as any).api_base.trim() !== '';
} Try / catch
try {
const res = await fetch('/api/models/fetch', {...});
if (res.status === 400 && (await res.text()).includes('No default API base')) {
/* mark api_base required in the form for this provider and block submit */
}
} catch (e) { /* network */ } Prevention
- Treat api_base as required for custom/self-hosted provider ids
- Trim api_base before sending — whitespace-only fails the check
- Include the /v1 suffix (or provider-correct prefix) in saved bases for OpenAI-compatible servers
When it happens
Trigger: POST /api/models/fetch with {"provider": "custom-openai-compatible"} (a provider type with no well-known default base) and no api_base; api_base sent as "" or only whitespace after trimming; a proxy provider that requires an explicit endpoint.
Common situations: Self-hosted LLM setups (ollama, vllm, llama.cpp) behind a custom provider id; frontend omits the api_base field for providers where it assumed a default exists; provider added via config with empty api_base.
Related errors
- Model %q cannot be used as the default chat model
- provider is required
- provider %q does not support model listing
- build request: %w
- Invalid JSON: %v
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/7771d3f4565ee9a6.
Report an issue: GitHub.