siyuan-note/siyuan · error

read body failed

Error message

read body failed: %s

What it means

WebFetch reads the response body via io.ReadAll(io.LimitReader(resp.Body, maxReadBytes)); any I/O error during that read (connection reset mid-body, TLS error, timeout while streaming, truncated chunked encoding) is wrapped as 'read body failed: <cause>'. Headers were received fine; the body transfer broke.

Solutions

  1. Retry the fetch — this is usually transient
  2. Inspect the wrapped cause after 'read body failed: ' for reset vs timeout specifics
  3. Prefer smaller, faster endpoints (the shorter the transfer, the lower the interruption risk)
  4. If persistent, check for middleboxes/proxies between the kernel and the target and raise client timeouts

Example fix

// before
content, err := WebFetch(url, "markdown")

// after
content, err := WebFetch(url, "markdown")
if err != nil && strings.Contains(err.Error(), "read body failed") {
    content, err = WebFetch(url, "markdown") // one retry for transient truncation
}
Defensive patterns

Strategy: retry

Try / catch

content, err := WebFetch(url, format)
if err != nil && strings.Contains(err.Error(), "read body failed") {
    time.Sleep(time.Second)
    content, err = WebFetch(url, format) // single retry for transient truncation
}

Prevention

When it happens

Trigger: Server closes the connection partway through the body; network interruption during a large transfer; read deadline hit on a slow body; proxy dropping a keep-alive connection mid-response.

Common situations: Flaky mobile/VPN connections; aggressive load balancers killing slow responses; fetching from servers that trickle data and trip timeouts; transient upstream failures behind CDNs.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/a91247c94c0f4b25. Report an issue: GitHub.

Appendix: source

Thrown at kernel/util/webfetch.go:74

	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		return "", fmt.Errorf("HTTP %d", resp.StatusCode)
	}

	contentType := resp.Header.Get("Content-Type")
	maxReadBytes := int64(maxWebFetchBytes)
	if !strings.HasPrefix(contentType, "text/html") && !strings.HasPrefix(contentType, "text/plain") {
		maxReadBytes = maxWebFetchFileBytes
	}
	if resp.ContentLength > maxReadBytes {
		return "", errors.New("response too large")
	}

	body, err := io.ReadAll(io.LimitReader(resp.Body, maxReadBytes))
	if err != nil {
		return "", errors.New("read body failed: " + err.Error())
	}

	if !strings.HasPrefix(contentType, "text/html") && !strings.HasPrefix(contentType, "text/plain") {
		importDir := filepath.Join(TempDir, "import")
		if merr := os.MkdirAll(importDir, 0755); merr != nil {
			return "", errors.New("create import dir failed: " + merr.Error())
		}
		filename := extractFilename(rawURL, contentType)
		filePath := filepath.Join(importDir, filename)
		if werr := os.WriteFile(filePath, body, 0644); werr != nil {
			return "", errors.New("write file failed: " + werr.Error())
		}
		return fmt.Sprintf("Saved to: %s (%d bytes)", filePath, len(body)), nil
	}

	htmlStr := string(body)

	isHTML := strings.HasPrefix(contentType, "text/html")

View on GitHub (pinned to 9f775e8a12)