siyuan-note/siyuan · error
read body failed: %s
Error message
read body failed: %s
What it means
Returned by HTTPRequest when io.ReadAll on the LimitReader-bounded body fails. At this point headers (status, Content-Type, Content-Length) were already read successfully; the failure is mid-stream while reading the response body. The underlying read error is appended for diagnostics.
Source
Thrown at kernel/util/httprequest.go:109
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 251596fc0d)
Solutions
- Retry the request once — mid-stream read failures are often transient.
- If recurring, reduce the requested payload (paginate) so the body completes before any connection idle timeout.
- Inspect the appended I/O error text to distinguish a reset ('connection reset') from a timeout ('i/o timeout').
Example fix
// before
_, _, _, err := util.HTTPRequest("GET", rawURL, nil, "")
// after
var err error
for attempt := 0; attempt < 2; attempt++ {
_, _, _, err = util.HTTPRequest("GET", rawURL, nil, "")
if err == nil || !strings.Contains(err.Error(), "read body failed") {
break
}
time.Sleep(500 * time.Millisecond)
} Defensive patterns
Strategy: retry
Try / catch
var err error
for attempt := 0; attempt < 2; attempt++ {
_, _, _, err = util.HTTPRequest(method, rawURL, headers, body)
if err == nil || !strings.Contains(err.Error(), "read body failed") { break }
time.Sleep(500 * time.Millisecond)
} Prevention
- Retry once on mid-stream read failures; they are usually transient.
- Narrow large payloads so the body completes before any idle timeout.
When it happens
Trigger: After receiving response headers, the connection drops or stalls while the body is still being read; the server closes the connection early; the LimitReader hits a transient I/O error. Note: because LimitReader is used, EOF after the cap is not an error — this fires only on an actual read failure.
Common situations: An unstable network link, a server that times out mid-response, a proxy that closes idle connections, or a server streaming a body larger than the cap and the connection being reset before truncation completes.
Related errors
- request failed: %s
- fetch failed: %s
- download custom emoji failed with status %d
- read custom emoji response failed: %w
- get stage index failed
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/3b94c6bdf23f0bdc.
Report an issue: GitHub.