siyuan-note/siyuan · error
list models HTTP
Error message
list models HTTP %d: %s
What it means
ListAvailableModelsWithContext wraps a non-2xx response from the provider's model-list endpoint, including the first 64KB of the response body. This is the primary path for surfacing provider rejections (auth failures, bad endpoints, rate limits) when calling ListAvailableModels or the listModelsContract handler. The body text is the provider's own error payload, so it usually states the exact reason.
Solutions
- Read the status and body text in the error: a 401 means fix the API key, 404 means fix the base URL, 429 means back off
- Verify the model list endpoint path matches the provider (most OpenAI-compatible providers expect <baseURL>/v1/models)
- Test the same request with curl using the configured base URL and key to isolate config vs network
- If the body is an HTML error page, the URL is hitting the wrong service (proxy/landing page), not the API
Example fix
// before baseURL := "https://api.example.com" // missing /v1 // after baseURL := "https://api.example.com/v1" // so the request hits <baseURL>/models correctly
Defensive patterns
Strategy: try-catch
Validate before calling
// before calling: verify key + base URL shape
if !strings.HasPrefix(apiKey, "sk-") { return errors.New("API key looks invalid") }
if !strings.HasSuffix(strings.TrimRight(baseURL, "/"), "/v1") { log.Println("base URL may need a /v1 suffix") } Try / catch
if err != nil {
msg := err.Error()
switch {
case strings.Contains(msg, "401"):
return fixAPIKeyError(msg)
case strings.Contains(msg, "429"):
return backoffAndRetry()
default:
return fmt.Errorf("provider rejected model list: %w", err)
}
} Prevention
- Store the base URL with the correct /v1 suffix and validate it on config save
- Rotate API keys before expiry and re-test the models endpoint after rotation
- Check provider status pages when seeing 5xx bodies
When it happens
Trigger: Calling ListAvailableModels/ListAvailableModelsWithContext where the HTTP status is < 200 or >= 400, e.g. 401 invalid API key, 404 wrong base URL, 429 rate limited, 500 provider outage.
Common situations: Expired or wrong API key in the AI model config; base URL missing or wrongly adding /v1; using an OpenAI-specific endpoint against a provider with a different path; quota exhausted; proxy returning an error page.
Related errors
- invalid AI provider HTTP headers
- list models HTTP
- rerank HTTP
- asset path [ ] does not match data path [ ]
- authentication probe returned HTTP " + response.status
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/a45512e5c8fa82a3.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/util/openai.go:392
req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint, nil)
if err != nil {
return nil, err
}
if apiKey != "" {
req.Header.Set("Authorization", "Bearer "+apiKey)
}
resp, err := newAIProviderHTTPClient(apiBaseURL, headers...).Do(req)
if err != nil {
logging.LogErrorf("list models [%s] failed: %s", apiBaseURL, err)
return
}
defer resp.Body.Close()
if resp.StatusCode < http.StatusOK || http.StatusBadRequest <= resp.StatusCode {
body, readErr := io.ReadAll(io.LimitReader(resp.Body, 64*1024))
if readErr != nil {
err = fmt.Errorf("list models HTTP %d", resp.StatusCode)
} else {
err = fmt.Errorf("list models HTTP %d: %s", resp.StatusCode, strings.TrimSpace(string(body)))
}
logging.LogErrorf("list models [%s] failed: %s", apiBaseURL, err)
return
}
payload := &availableModelsPayload{}
if err = json.NewDecoder(resp.Body).Decode(payload); err != nil {
logging.LogErrorf("decode models [%s] failed: %s", apiBaseURL, err)
return
}
for _, item := range payload.Data {
id := strings.TrimSpace(item.ID)
if id == "" {
continue
}
models = append(models, AvailableModel{
ID: id,
ContextLength: firstValidModelContextLength(View on GitHub (pinned to 9f775e8a12)