Tencent/WeKnora · error

read body: %w

Error message

read body: %w

What it means

This error wraps an I/O failure while reading the remote image response body through an io.LimitReader capped at maxRemoteImageSize+1 bytes. It signals the body stream broke mid-read (connection reset, unexpected EOF, timeout) rather than a size violation.

Source

Thrown at internal/infrastructure/docparser/image_resolver.go:1041

	}

	// Determine MIME type from Content-Type header.
	ct := resp.Header.Get("Content-Type")
	mimeType, _, _ = mime.ParseMediaType(ct)
	if mimeType == "" {
		mimeType = "application/octet-stream"
	}

	// Only allow image content types (or octet-stream which we sniff later).
	if !strings.HasPrefix(mimeType, "image/") && mimeType != "application/octet-stream" {
		return nil, "", fmt.Errorf("non-image content type: %s", mimeType)
	}

	// Read body with size limit.
	limited := io.LimitReader(resp.Body, maxRemoteImageSize+1)
	body, err := io.ReadAll(limited)
	if err != nil {
		return nil, "", fmt.Errorf("read body: %w", err)
	}
	if len(body) > maxRemoteImageSize {
		return nil, "", fmt.Errorf("image exceeds %d bytes limit", maxRemoteImageSize)
	}

	// If MIME was octet-stream, sniff the real type from body.
	if mimeType == "application/octet-stream" {
		detected := http.DetectContentType(body)
		if strings.HasPrefix(detected, "image/") {
			mimeType = detected
		} else {
			return nil, "", fmt.Errorf("downloaded data is not an image (sniffed: %s)", detected)
		}
	}

	return body, mimeType, nil
}

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Unwrap the error — connection reset/unexpected EOF usually means retry with backoff
  2. Increase the download timeout/context budget for large images
  3. Check proxy/LB idle-timeout settings that may cut long transfers
  4. Skip persistently failing image URLs rather than blocking document processing

Example fix

// before
limited := io.LimitReader(resp.Body, maxRemoteImageSize+1)
body, err := io.ReadAll(limited)
if err != nil { return nil, "", fmt.Errorf("read body: %w", err) }
// after
limited := io.LimitReader(resp.Body, maxRemoteImageSize+1)
body, err := io.ReadAll(limited)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) {
        return nil, "", fmt.Errorf("read body (deadline exceeded): %w", err)
    }
    return nil, "", fmt.Errorf("read body: %w", err)
}
Defensive patterns

Strategy: retry

Validate before calling

// ensure enough time budget for large images before fetching
if deadline, ok := ctx.Deadline(); ok && time.Until(deadline) < 10*time.Second {
    var cancel context.CancelFunc
    ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()
}

Try / catch

data, mimeType, err := downloadImage(ctx, client, remoteURL)
if err != nil {
    var ne net.Error
    if errors.As(err, &ne) && (ne.Timeout() || strings.Contains(err.Error(), "unexpected EOF")) {
        // transient truncation: retry once with backoff
        time.Sleep(time.Second)
        return downloadImage(ctx, client, remoteURL)
    }
    return nil, "", fmt.Errorf("read body: %w", err)
}

Prevention

When it happens

Trigger: io.ReadAll on the limited body fails: connection reset by peer, unexpected EOF, TLS truncation, or context deadline exceeded while streaming a large image.

Common situations: Flaky image hosts dropping connections on large files, proxies terminating long downloads, context timeout too short for big images, network interruptions mid-transfer.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/67c02dad3cd6db1c. Report an issue: GitHub.