siyuan-note/siyuan · error

read body failed:

Error message

read body failed: 

What it means

HTTPRequest reads the response body with io.ReadAll(io.LimitReader(resp.Body, maxReadBytes)). If that read fails — typically a network interruption mid-body, a TLS/connection reset, or the read hitting the 30-second client timeout — the error is wrapped as "read body failed: <cause>". This happens after a successful response header, so statusCode and contentType may already be set.

Source

Thrown at kernel/util/httprequest.go:356

		return 0, "", "", errors.New("nil response")
	}
	defer resp.Body.Close()

	statusCode = resp.StatusCode
	contentType = resp.Header.Get("Content-Type")

	maxReadBytes := int64(maxHTTPRequestBytes)
	if !isTextContentType(contentType) {
		maxReadBytes = maxHTTPRequestFileBytes
	}
	// ContentLength 为 -1(chunked)时跳过大小预检,交由 LimitReader 兜底截断。
	if resp.ContentLength > maxReadBytes {
		return statusCode, contentType, "", errors.New("response too large")
	}

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

	// 二进制响应落盘,返回文件路径,供智能体按需进一步处理。
	if !isTextContentType(contentType) {
		importDir := filepath.Join(TempDir, "import")
		if merr := os.MkdirAll(importDir, 0755); merr != nil {
			return statusCode, contentType, "", errors.New("create import dir failed: " + merr.Error())
		}
		filename := extractFilename(rawURL, contentType)
		filePath := filepath.Join(importDir, filename)
		if werr := os.WriteFile(filePath, respBody, 0644); werr != nil {
			return statusCode, contentType, "", errors.New("write file failed: " + werr.Error())
		}
		return statusCode, contentType, fmt.Sprintf("Saved to: %s (%d bytes)", filePath, len(respBody)), nil
	}

	return statusCode, contentType, truncateRunes(string(respBody), maxHTTPRequestChars), nil
}

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Retry the request — transient network failures are the most common cause
  2. Check the wrapped cause after "read body failed: " (context deadline exceeded, connection reset, unexpected EOF) and address it specifically
  3. Reduce response size (pagination) so the body transfer completes within the 30-second client timeout
  4. Verify proxy stability if HTTP_PROXY/SOCKS5 proxies are configured, since tunnel drops surface here
  5. If it consistently fails for one host, test the URL with curl to determine whether the server itself aborts transfers

Example fix

// before: single attempt
status, ct, text, err := util.HTTPRequest("GET", url, nil, "")
// after: retry transient failures
var status int; var ct, text string; var err error
for i := 0; i < 3; i++ {
    status, ct, text, err = util.HTTPRequest("GET", url, nil, "")
    if err == nil || !strings.Contains(err.Error(), "read body failed") { break }
    time.Sleep(time.Second)
}
Defensive patterns

Strategy: retry

Try / catch

var status int; var ct, text string; var err error
for attempt := 0; attempt < 3; attempt++ {
    status, ct, text, err = util.HTTPRequest("GET", url, nil, "")
    if err == nil || !strings.Contains(err.Error(), "read body failed") {
        break
    }
    time.Sleep(time.Duration(attempt+1) * time.Second)
}

Prevention

When it happens

Trigger: HTTPRequest() where the connection drops or times out while streaming the response body, or the server closes the connection prematurely (chunked encoding aborted), or a proxy tunnel is severed mid-transfer.

Common situations: Flaky Wi-Fi/mobile networks, proxies killing long transfers, servers with keep-alive races, or responses taking longer than the 30-second ssrfSafeClient timeout on slow links.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/b589bd89da2342f0. Report an issue: GitHub.