siyuan-note/siyuan · error

list models HTTP

Error message

list models HTTP %d

What it means

ListAvailableModelsWithContext in kernel/util/openai.go aborts when the models-list endpoint returns an HTTP status outside 200-399 and the response body could not be read (io.ReadAll on a 64KB-limited reader failed). The status code is reported instead of the body, because no body content could be captured. It is a defensive branch that ensures the caller always gets an HTTP-status error even when the body is unreadable.

Solutions

  1. Check the status code in the error message and verify the configured API base URL and key are correct for the provider
  2. Retry the request; the failure is often a transient network truncation rather than a server-side rejection
  3. Verify network path (proxy, VPN, firewall) between the kernel and apiBaseURL; run the equivalent curl -v request to compare
  4. Inspect kernel logs for the adjacent 'list models [%s] failed' line to see which URL was contacted

Example fix

// before: cannot distinguish provider message because body read failed
err = fmt.Errorf("list models HTTP %d", resp.StatusCode)
// after: log the read error too so the root cause is visible
if readErr != nil {
    err = fmt.Errorf("list models HTTP %d (body read failed: %v)", resp.StatusCode, readErr)
}
Defensive patterns

Strategy: retry

Validate before calling

if resp.StatusCode < 200 || resp.StatusCode >= 400 { log.Printf("models endpoint unhealthy: HTTP %d", resp.StatusCode) }

Try / catch

if err != nil {
    if strings.HasPrefix(err.Error(), "list models HTTP ") {
        // retry with backoff; do not treat as a config error
        return retryWithBackoff(call)
    }
    return err
}

Prevention

When it happens

Trigger: Calling ListAvailableModels/ListAvailableModelsWithContext (or the listModelsContract API handler) against an AI provider whose /models endpoint responds with a non-2xx status while the response body is truncated, connection-reset mid-body, or otherwise unreadable.

Common situations: Proxy or gateway cutting the error response short; TLS/network interruption right after headers; server closing the connection immediately on 429/503; misconfigured apiBaseURL pointing at a non-OpenAI-compatible service that drops the body.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/7654be772a6b59c8. Report an issue: GitHub.

Appendix: source

Thrown at kernel/util/openai.go:390

	endpoint := strings.TrimRight(apiBaseURL, "/") + "/models"
	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{

View on GitHub (pinned to 9f775e8a12)