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, err

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Retry the install; transient mid-stream failures often succeed on a second attempt.
  2. Switch to a smaller or single-file SKILL.md source to reduce transfer size.
  3. Stabilize the network connection or disable an aggressive proxy.
  4. 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

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


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/2006269e1077bf57. Report an issue: GitHub.