{"record":{"id":"a652391171e3ab30","repo":"siyuan-note/siyuan","slug":"web-search-failed-s","errorCode":null,"errorMessage":"web search failed: %s","messagePattern":"web search failed: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/util/websearch.go","lineNumber":83,"sourceCode":"\n\treqBody := mcpRequest{\n\t\tJSONRPC: \"2.0\",\n\t\tID:      1,\n\t\tMethod:  \"tools/call\",\n\t\tParams: mcpParams{\n\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","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/util/websearch.go#L65-L101","documentation":"Returned at websearch.go:83 when httpclient.NewBrowserRequest().Post(exaURL) fails at the transport layer before any response body is available. exaURL defaults to https://mcp.exa.ai/mcp (websearch.go:31), appended with ?exaApiKey=... when a key is supplied (websearch.go:62-64). An empty key still issues the request, just unauthenticated. The %s is the underlying transport error.","triggerScenarios":"Calling util.WebSearch(query, key) with no network, mcp.exa.ai unresolvable, TLS handshake failure, proxy pointing at a dead upstream, or an egress firewall blocking the Exa host.","commonSituations":"Air-gapped/offline host, corporate firewall blocking mcp.exa.ai, broken HTTP_PROXY, captive portal intercepting TLS, IPv6 connectivity problems.","solutions":["Confirm the host is reachable from the SiYuan host: `curl -I https://mcp.exa.ai/mcp`.","Verify internet connectivity and DNS for mcp.exa.ai.","Check/correct HTTP_PROXY / HTTPS_PROXY env vars.","Retry once for transient network blips."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Confirm the Exa MCP host is reachable before issuing the search.\nfunc exaReachable() error {\n    if _, err := net.LookupHost(\"mcp.exa.ai\"); err != nil {\n        return fmt.Errorf(\"dns: %w\", err)\n    }\n    return nil\n}","typeGuard":"func isWebSearchFailed(err error) bool {\n    return err != nil && strings.HasPrefix(err.Error(), \"web search failed:\")\n}","tryCatchPattern":"out, err := util.WebSearch(q, key)\nif err != nil && isWebSearchFailed(err) {\n    // transport-level: back off and retry once\n    out, err = util.WebSearch(q, key)\n}","preventionTips":["Verify mcp.exa.ai is reachable from the host (curl) before relying on web search.","Keep proxy env vars correct; Exa is an external HTTPS endpoint.","Treat web-search-failed as transient and retry once with backoff."],"tags":["network","search","mcp","exa","transport"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}