{"record":{"id":"a91247c94c0f4b25","repo":"siyuan-note/siyuan","slug":"read-body-failed-s-a91247","errorCode":null,"errorMessage":"read body failed: %s","messagePattern":"read body failed: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/webfetch.go","lineNumber":75,"sourceCode":"\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode >= 400 {\n\t\treturn \"\", fmt.Errorf(\"HTTP %d\", resp.StatusCode)\n\t}\n\n\tcontentType := resp.Header.Get(\"Content-Type\")\n\tmaxReadBytes := int64(maxWebFetchBytes)\n\tif !strings.HasPrefix(contentType, \"text/html\") && !strings.HasPrefix(contentType, \"text/plain\") {\n\t\tmaxReadBytes = maxWebFetchFileBytes\n\t}\n\tif resp.ContentLength > maxReadBytes {\n\t\treturn \"\", errors.New(\"response too large\")\n\t}\n\n\tbody, err := io.ReadAll(io.LimitReader(resp.Body, maxReadBytes))\n\tif err != nil {\n\t\treturn \"\", errors.New(\"read body failed: \" + err.Error())\n\t}\n\n\tif !strings.HasPrefix(contentType, \"text/html\") && !strings.HasPrefix(contentType, \"text/plain\") {\n\t\timportDir := filepath.Join(TempDir, \"import\")\n\t\tif merr := os.MkdirAll(importDir, 0755); merr != nil {\n\t\t\treturn \"\", errors.New(\"create import dir failed: \" + merr.Error())\n\t\t}\n\t\tfilename := extractFilename(rawURL, contentType)\n\t\tfilePath := filepath.Join(importDir, filename)\n\t\tif werr := os.WriteFile(filePath, body, 0644); werr != nil {\n\t\t\treturn \"\", errors.New(\"write file failed: \" + werr.Error())\n\t\t}\n\t\treturn fmt.Sprintf(\"Saved to: %s (%d bytes)\", filePath, len(body)), nil\n\t}\n\n\thtmlStr := string(body)\n\n\tisHTML := strings.HasPrefix(contentType, \"text/html\")","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/util/webfetch.go#L57-L93","documentation":"Returned at webfetch.go:75 when io.ReadAll(io.LimitReader(resp.Body, maxReadBytes)) fails. Headers were received fine, but the body stream broke partway through. The %s is the io error (e.g. connection reset, read deadline, unexpected EOF).","triggerScenarios":"Connection reset by peer mid-download, read timeout (client or server side), server closing the keep-alive socket early, TLS renegotiation failure, or the process hitting its read deadline on a slow link.","commonSituations":"Unstable mobile/tethered network, slow CDN that times out, server killing slow clients, intermediary (proxy/NAT) dropping idle connections.","solutions":["Retry the fetch once — body-stream errors are frequently transient.","If repeatable, test with `curl --limit-rate` and a longer timeout to characterise the drop.","Check whether a proxy/VPN in the path is killing long responses.","For very slow links, raise the httpclient read timeout if configurable in your build."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"func isReadBodyFailed(err error) bool {\n    return err != nil && strings.HasPrefix(err.Error(), \"read body failed:\")\n}","tryCatchPattern":"out, err := util.WebFetch(raw, \"markdown\")\nif err != nil && isReadBodyFailed(err) {\n    // mid-stream drop: retry once\n    out, err = util.WebFetch(raw, \"markdown\")\n}","preventionTips":["Body-stream breaks are usually transient — retry once.","On flaky links, characterise the drop with curl and a longer timeout before blaming content.","Keep read timeouts generous enough for slow CDNs."],"tags":["network","io","fetch","stream"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}