sipeed/picoclaw · error
read error response: %w
Error message
read error response: %w
What it means
After receiving a non-200 response, reading its body (capped at 512 bytes via io.LimitReader) failed. The server sent a status line but the connection broke before the error body could be read, so the code wraps and returns the read error instead of the status diagnostics.
Source
Thrown at cmd/picoclaw/internal/model/online.go:50
url := strings.TrimRight(baseURL, "/") + "/models"
client := &http.Client{Timeout: 15 * time.Second}
req, err := http.NewRequest(http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("build request: %w", err)
}
req.Header.Set("Authorization", "Bearer "+apiKey)
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, readErr := io.ReadAll(io.LimitReader(resp.Body, 512))
if readErr != nil {
return nil, fmt.Errorf("read error response: %w", readErr)
}
return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, strings.TrimSpace(string(body)))
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("read response: %w", err)
}
// {"data": [...]} envelope. Distinguish "envelope shape with empty list"
// from "object without a data key" via Data being non-nil after unmarshal:
// json.Unmarshal sets Data to []modelEntry{} for `{"data":[]}` but leaves
// it as nil when "data" is absent or null.
var envelope modelsAPIResponse
if err := json.Unmarshal(body, &envelope); err == nil && envelope.Data != nil {
return envelope.Data, nil
}
View on GitHub (pinned to 49183d7e8d)
Solutions
- Retry the operation — mid-body drops on error responses are nearly always transient.
- If it reproduces, capture the exchange with curl -v to see where the body is cut.
- Report to the provider if every error response dies mid-body.
Defensive patterns
Strategy: retry
Try / catch
body, readErr := io.ReadAll(io.LimitReader(resp.Body, 512))
if readErr != nil {
if isTransientConnError(readErr) { // net.ErrClosed / syscall.ECONNRESET
return retryWithBackoff()
}
return fmt.Errorf("read error response: %w", readErr)
} Prevention
- Classify mid-body read failures as transient and retry once before surfacing.
- Log resp.StatusCode alongside the read error to identify offending intermediaries.
When it happens
Trigger: The connection is closed or reset between the response headers and body of an error response while io.ReadAll(io.LimitReader(resp.Body, 512)) runs at online.go:50 — e.g. an intermediary RST after sending a 4xx/5xx status.
Common situations: Flaky upstream, an aggressive load balancer resetting errored connections, or a proxy that truncates error bodies; rare and almost always transient.
Related errors
- read response: %w
- failed to get WeCom QR code: %w
- unexpected status %s
- request failed: %w
- reading usage response: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/62b65dce233f0cf0.
Report an issue: GitHub.