chenhg5/cc-connect · error

read full body: %w

Error message

read full body: %w

What it means

Wraps an io.ReadAll failure while reading a full 200 body (server ignored Range) up to resourceMaxBytes+1 bytes in resourceFetchFirstChunk. Same class of transport failure as the first-chunk read error but on the whole-body path; it can also fire when the download context is cancelled while streaming a large body.

Source

Thrown at platform/feishu/resource_download.go:173

	case http.StatusPartialContent:
		cr := resp.Header.Get("Content-Range")
		total, ok := parseContentRangeTotal(cr)
		if !ok {
			return nil, 0, fmt.Errorf("first-chunk: 206 without parseable Content-Range %q", cr)
		}
		body, err := io.ReadAll(io.LimitReader(resp.Body, 1))
		if err != nil {
			return nil, 0, fmt.Errorf("read first-chunk body: %w", err)
		}
		return body, total, nil

	case http.StatusOK:
		// Server ignored Range and sent the full body. We deliberately
		// honour it and skip the chunked loop — this matches pre-#1741
		// behaviour for files small enough that Feishu doesn't truncate.
		body, err := io.ReadAll(io.LimitReader(resp.Body, p.resourceMaxBytes+1))
		if err != nil {
			return nil, 0, fmt.Errorf("read full body: %w", err)
		}
		if int64(len(body)) > p.resourceMaxBytes {
			return nil, 0, fmt.Errorf("resource too large: body exceeds cap %d", p.resourceMaxBytes)
		}
		return body, 0, nil

	default:
		return nil, 0, fmt.Errorf("first-chunk: unexpected status %d", resp.StatusCode)
	}
}

// resourceFetchRemainingChunks loops Range GETs starting after the first
// byte, concatenates them with `first`, and verifies the total size matches
// what the probe advertised.
func (p *Platform) resourceFetchRemainingChunks(ctx context.Context, token, messageID, fileKey, resType string, total int64, first []byte) ([]byte, error) {
	buf := bytes.NewBuffer(make([]byte, 0, total))
	buf.Write(first)

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Retry the download once connectivity is stable
  2. Check for idle-connection killing middleboxes; raise keepalive/idle timeouts
  3. Ensure callers don't cancel the context prematurely for large files

Example fix

null
Defensive patterns

Strategy: retry

Validate before calling

null

Type guard

null

Try / catch

if err != nil && strings.Contains(err.Error(), "read full body") {
    select {
    case <-ctx.Done(): return ctx.Err()
    case <-time.After(retryDelay):
        return retryDownload(ctx, msgID, key, resType)
    }
}

Prevention

When it happens

Trigger: Server returned 200 with the full body and the stream breaks mid-read: connection reset, TLS truncation, parent context cancelled, or read deadline hit on a large slow transfer.

Common situations: Downloading large files over unstable networks, NAT/firewall idle timeouts dropping long-lived connections, or user-triggered cancellation (message superseded) aborting the stream.

Related errors


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