chenhg5/cc-connect · error

read body from %s: %w

Error message

read body from %s: %w

What it means

fetchSkillPresetsFromURL wraps io.ReadAll failures of the response body with "read body from %s: %w". The body is read through io.LimitReader(resp.Body, 1<<20) (1 MiB cap), so this fires when the connection breaks mid-read or the reader returns a non-EOF error.

Source

Thrown at core/skill_presets.go:133

	return c.data, nil
}

func fetchSkillPresetsFromURL(url string, timeout time.Duration) (*SkillPresetsResponse, error) {
	slog.Debug("fetching skill presets", "url", url)
	client := &http.Client{Timeout: timeout}
	resp, err := client.Get(url)
	if err != nil {
		return nil, fmt.Errorf("HTTP GET %s: %w", url, err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("HTTP GET %s: status %d", url, resp.StatusCode)
	}

	body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
	if err != nil {
		return nil, fmt.Errorf("read body from %s: %w", url, err)
	}

	var result SkillPresetsResponse
	if err := json.Unmarshal(body, &result); err != nil {
		return nil, fmt.Errorf("parse JSON from %s: %w", url, err)
	}
	return &result, nil
}

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Retry the fetch — this is usually transient; other configured sources may succeed
  2. Increase client.Timeout if slow-but-valid sources are being cut off mid-body, though the 1 MiB read cap stays
  3. Check proxy/LB logs for early connection termination
  4. Prefer a closer/faster mirror for the presets source

Example fix

// before
resp, err := client.Get(url) // default timeout too short for slow mirror
// after
client := &http.Client{Timeout: 60 * time.Second}
resp, err := client.Get(url)
Defensive patterns

Strategy: retry

Validate before calling

resp, err := http.Get(sourceURL)
if err == nil {
    n, err := io.Copy(io.Discard, io.LimitReader(resp.Body, 1<<20))
    if err != nil { return fmt.Errorf("body unreadable from %s: %w", sourceURL, err) }
    if n == 0 { return fmt.Errorf("empty body from %s", sourceURL) }
}

Try / catch

var presets *SkillPresetsResponse
var lastErr error
for attempt := 0; attempt < 3; attempt++ {
    presets, lastErr = fetchSkillPresetsFromURL(url, timeout)
    if lastErr == nil { break }
    if strings.HasPrefix(lastErr.Error(), "read body from") {
        time.Sleep(time.Duration(attempt+1) * time.Second)
    }
}
if lastErr != nil { slog.Error("failed reading presets body", "url", url, "err", lastErr) }

Prevention

When it happens

Trigger: Connection reset or timeout while streaming the presets body; server closing the connection early (truncated response); TLS/proxy errors mid-transfer during fetch.

Common situations: Flaky Wi-Fi/mobile links; aggressive proxies killing long responses; very large or stalled responses from a misbehaving mirror.

Related errors


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/107830896446ab7d. Report an issue: GitHub.