{"record":{"id":"13885c22782996ca","repo":"siyuan-note/siyuan","slug":"web-search-read-response-failed-s","errorCode":null,"errorMessage":"web search read response failed: %s","messagePattern":"web search read response failed: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/websearch.go","lineNumber":89,"sourceCode":"\t\t\tName: \"web_search_exa\",\n\t\t\tArguments: map[string]any{\n\t\t\t\t\"query\":      query,\n\t\t\t\t\"type\":       \"auto\",\n\t\t\t\t\"numResults\": 8,\n\t\t\t\t\"livecrawl\":  \"fallback\",\n\t\t\t},\n\t\t},\n\t}\n\n\tresp, err := httpclient.NewBrowserRequest().SetHeader(\"Accept\", \"application/json, text/event-stream\").SetBody(reqBody).Post(exaURL)\n\tif err != nil {\n\t\treturn \"\", errors.New(\"web search failed: \" + err.Error())\n\t}\n\tdefer resp.Body.Close()\n\n\tbodyBytes, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", errors.New(\"web search read response failed: \" + err.Error())\n\t}\n\tbody := string(bodyBytes)\n\n\tpreview := body\n\tif len(preview) > 500 {\n\t\tpreview = body[:500]\n\t}\n\tlogging.LogInfof(\"websearch response: status=%d, len=%d, preview=%s\", resp.StatusCode, len(body), preview)\n\n\ttext := parseMcpResponse(body)\n\tif text == \"\" {\n\t\treturn \"No search results found. Please try a different query.\", nil\n\t}\n\n\treturn truncateRunes(text, maxWebSearchChars), nil\n}\n\nfunc parseMcpResponse(body string) string {","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/util/websearch.go#L71-L107","documentation":"Returned at websearch.go:89 when io.ReadAll(resp.Body) fails after the POST to Exa connected and headers came back. Unlike WebFetch, this reader has NO size limit applied — the entire response body is read into memory. The %s is the stream read error.","triggerScenarios":"Connection reset mid-stream, read timeout while Exa's livecrawl is still producing output, server closing the SSE/streaming channel early, or memory pressure on an unusually large response.","commonSituations":"Slow/unstable network during a long livecrawl, intermediary dropping long-lived responses, Exa returning an unexpectedly large payload, client read deadline too tight.","solutions":["Retry the search — stream drops are often transient.","Stabilise the network path (disable flaky VPN, switch off aggressive proxy timeouts).","Be aware there is no body-size cap; on pathological responses the process can grow large before failing.","If repeatable, capture resp.StatusCode (logged at websearch.go:97) to distinguish a server-side truncation."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"func isWebSearchReadFailed(err error) bool {\n    return err != nil && strings.HasPrefix(err.Error(), \"web search read response failed:\")\n}","tryCatchPattern":"out, err := util.WebSearch(q, key)\nif err != nil && isWebSearchReadFailed(err) {\n    // mid-stream drop, often transient\n    out, err = util.WebSearch(q, key)\n}","preventionTips":["Retry once — body-stream drops during livecrawl are frequently transient.","Be aware WebSearch reads the whole body with no size cap; monitor memory on pathological responses.","Stabilise the network path (VPN/proxy timeouts) for long-running searches."],"tags":["network","io","search","stream","exa"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}