siyuan-note/siyuan · error
read body failed: %s
Error message
read body failed: %s
What it means
Thrown by fetchBytes when io.ReadAll fails while streaming the response body (after headers were received and status was < 400). The body read is capped at maxSkillDownloadBytes+1 via io.LimitReader. This indicates the connection broke mid-transfer or the reader errored.
Source
Thrown at kernel/util/skill.go:414
return nil, "", err
}
// fetchBytes 执行带大小限制的 GET
func fetchBytes(rawURL string) (data []byte, contentType string, err error) {
resp, err := httpclient.NewBrowserRequest().Get(rawURL)
if err != nil {
return nil, "", errors.New("download failed: " + err.Error())
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
return nil, "", fmt.Errorf("download failed: HTTP %d", resp.StatusCode)
}
contentType = resp.Header.Get("Content-Type")
body, err := io.ReadAll(io.LimitReader(resp.Body, maxSkillDownloadBytes+1))
if err != nil {
return nil, "", errors.New("read body failed: " + err.Error())
}
if len(body) > maxSkillDownloadBytes {
return nil, "", errors.New("skill source too large (limit 10MB)")
}
return body, contentType, nil
}
// installFromZip 解压 zip 并安装其中的 skill
func installFromZip(data []byte) (*InstallSkillResult, error) {
tmpRoot := filepath.Join(TempDir, "ai", "skill-install", gulu.Rand.String(7))
if err := os.MkdirAll(tmpRoot, 0755); err != nil {
return nil, err
}
defer os.RemoveAll(tmpRoot)
zipPath := filepath.Join(tmpRoot, "src.zip")
if err := os.WriteFile(zipPath, data, 0644); err != nil {
return nil, errView on GitHub (pinned to 251596fc0d)
Solutions
- Retry the install; transient mid-stream failures often succeed on a second attempt.
- Switch to a smaller or single-file SKILL.md source to reduce transfer size.
- Stabilize the network connection or disable an aggressive proxy.
- If persistent, fetch the URL directly with curl to isolate server vs. client.
Defensive patterns
Strategy: retry
Try / catch
// retry mid-stream read failures a limited number of times
for i := 0; i < 2; i++ {
res, err = util.InstallSkill(src)
if err == nil || !strings.HasPrefix(err.Error(), "read body failed") {
break
}
} Prevention
- Prefer smaller sources (single-file SKILL.md or a narrow subdirectory) on flaky networks.
- Retry once before reporting; body-read errors are often transient.
- Isolate persistent failures by fetching the URL with curl from the same host.
When it happens
Trigger: The server closed the connection before sending the full body; a network drop during a large download; an io error from the LimitReader; the response uses chunked encoding that terminates prematurely.
Common situations: Unstable connection on large zip archives; mobile/flaky network; server-side timeouts on slow transfers; intermediary (proxy/CDN) resetting the connection.
Related errors
- read custom emoji response failed: %w
- read body failed: %s
- unsupported skill source (content-type: %s); expected a zip
- invalid download URL: %s
- download failed (tried main and master): %v
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/2006269e1077bf57.
Report an issue: GitHub.