chenhg5/cc-connect · error
read resource: %w
Error message
read resource: %w
What it means
io.ReadAll on the resource response body failed while reading the file content in resourceSingleGet (wrapped as "read resource"). This is a mid-body I/O error: connection reset, unexpected EOF, context cancellation, or a proxy truncating the stream.
Source
Thrown at platform/feishu/resource_download.go:280
if err != nil {
return nil, fmt.Errorf("resource request: %w", err)
}
defer func() { _ = resp.Body.Close() }()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
body, _ := io.ReadAll(io.LimitReader(resp.Body, 4*1024))
return nil, fmt.Errorf("resource API status=%d body=%q", resp.StatusCode, strings.TrimSpace(string(body)))
}
if cl := resp.ContentLength; cl > p.resourceMaxBytes {
return nil, fmt.Errorf("resource too large: Content-Length=%d exceeds cap %d", cl, p.resourceMaxBytes)
}
// LimitReader caps the body too in case the server lies about
// Content-Length; we read up to cap+1 bytes to detect the lie.
data, err := io.ReadAll(io.LimitReader(resp.Body, p.resourceMaxBytes+1))
if err != nil {
return nil, fmt.Errorf("read resource: %w", err)
}
if int64(len(data)) > p.resourceMaxBytes {
return nil, fmt.Errorf("resource too large: body exceeds cap %d", p.resourceMaxBytes)
}
slog.Debug(p.tag()+": resource downloaded (single GET)",
"file_key", fileKey, "type", resType, "size", len(data))
return data, nil
}
// resourceRangeChunk fetches a single byte range and verifies the
// Content-Range header agrees with what we asked for. Returns the bytes
// received; caller is responsible for ordering into the final buffer.
//
// Transient errors are retried with backoff; non-transient errors (4xx, 5xx
// other than the documented transient cases) fail fast.
func (p *Platform) resourceRangeChunk(
ctx context.Context,
token, messageID, fileKey, resType string,View on GitHub (pinned to 4000b2338a)
Solutions
- Retry the download — transient mid-body failures usually succeed on a second attempt
- Inspect the wrapped cause: context.DeadlineExceeded → increase ctx timeout; unexpected EOF → network/proxy issue
- Disable aggressive idle timeouts on proxies/LBs in front of egress, or raise their read timeout
- Ensure the HTTP client transport sets sensible timeouts (ResponseHeaderTimeout vs overall) so slow bodies aren't cut off
- For large files prefer the chunked path, which retries transient errors per chunk
Example fix
// before ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) // after: allow time for large bodies ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
Defensive patterns
Strategy: retry
Try / catch
data, err := p.resourceSingleGet(ctx, token, msgID, fileKey, resType)
if err != nil {
if strings.Contains(err.Error(), "read resource:") {
return backoffRetry(ctx, 3, download) // transient mid-body IO error
}
return err
} Prevention
- Use generous context timeouts proportional to file size
- Configure transport keep-alives and avoid aggressive proxy read timeouts
- Retry mid-body failures automatically with exponential backoff
When it happens
Trigger: resourceSingleGet got a 2xx response, passed the Content-Length check, but the body read failed — server closed the connection mid-transfer, ctx was cancelled, TLS/connection error mid-stream, or a proxy dropped the connection.
Common situations: Unstable network on long transfers; idle-connection timeouts or LB/proxy read timeouts cutting off a slow body; ctx deadline expiring while reading a large body; Feishu edge closing connections under load.
Related errors
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/db934182ae3655a5.
Report an issue: GitHub.