siyuan-note/siyuan · error

failed to read response body: %w

Error message

failed to read response body: %w

What it means

io.ReadAll returned an error while consuming the HTTP response body in siyuan.client.fetch. The %w wraps the underlying read error. The request was sent and a response started, but the body stream broke before it could be fully read.

Source

Thrown at kernel/plugin/api_client.go:141

				}
				r.SetHeader(model.XAuthTokenKey, p.token)

				if bodyString != nil {
					r.SetBody(*bodyString)
				} else if bodyBytes != nil {
					r.SetBody(*bodyBytes)
				}

				resp, sendErr := r.Send(method, targetURL)
				if sendErr != nil {
					err = sendErr
					return
				}

				defer resp.Body.Close()
				body, readErr := io.ReadAll(resp.Body)
				if readErr != nil {
					err = fmt.Errorf("failed to read response body: %w", readErr)
					return
				}

				responseHeader := map[string]string{}
				for k, vs := range resp.Header {
					responseHeader[k] = strings.Join(vs, ", ")
				}

				runErr := p.worker.Run(func(rt *goja.Runtime) (result any, err error) {
					response := rt.NewObject()
					lo.Must0(response.Set("url", rt.ToValue(path)))
					lo.Must0(response.Set("ok", rt.ToValue(resp.StatusCode >= 200 && resp.StatusCode < 300)))
					lo.Must0(response.Set("status", rt.ToValue(resp.StatusCode)))
					lo.Must0(response.Set("statusText", rt.ToValue(resp.Status)))
					lo.Must0(response.Set("headers", rt.ToValue(responseHeader)))
					lo.Must0(ObjectSetDataMethods(p, rt, response, body))
					result = response
					return

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Retry the request once — transient body-read failures usually succeed on retry.
  2. Verify the kernel is still running (check the ServerPort health endpoint) before retrying.
  3. For large payloads, reduce response size or paginate to lower the chance of a mid-stream break.

Example fix

async function fetchRetry(path, init, tries = 2) {
  for (let i = 0; i < tries; i++) {
    try { return await siyuan.client.fetch(path, init); }
    catch (e) { if (i === tries - 1) throw e; }
  }
}
Defensive patterns

Strategy: retry

Try / catch

try {
  const res = await siyuan.client.fetch('/api/x');
} catch (e) {
  if (/failed to read response body/.test(String(e))) {
    // retry — transient mid-stream break
  }
}

Prevention

When it happens

Trigger: The kernel closed/reset the loopback connection mid-response, the local server shut down during a streaming or large response, or the underlying TCP connection was dropped.

Common situations: Kernel restart during a plugin request, very large responses timing out, or the OS closing the loopback socket under resource pressure.

Related errors


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