{"record":{"id":"97fe39117a1ed048","repo":"Tencent/WeKnora","slug":"failed-to-unmarshal-exa-response-w","errorCode":null,"errorMessage":"failed to unmarshal Exa response: %w","messagePattern":"failed to unmarshal Exa response: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/infrastructure/web_search/exa.go","lineNumber":111,"sourceCode":"\n\tlogger.Infof(ctx, \"[WebSearch][Exa] query=%q maxResults=%d url=%s\", query, maxResults, p.baseURL)\n\tresp, err := p.client.Do(req)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to execute Exa request: %w\", err)\n\t}\n\tdefer resp.Body.Close()\n\tbody, err := io.ReadAll(io.LimitReader(resp.Body, maxExaResponseBytes))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read Exa response: %w\", err)\n\t}\n\tif resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {\n\t\tlogger.Warnf(ctx, \"[WebSearch][Exa] API returned status %d: %s\", resp.StatusCode, string(body))\n\t\treturn nil, fmt.Errorf(\"exa API returned status %d: %s\", resp.StatusCode, string(body))\n\t}\n\n\tvar data exaSearchResponse\n\tif err := json.Unmarshal(body, &data); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to unmarshal Exa response: %w\", err)\n\t}\n\tif data.Error != \"\" {\n\t\treturn nil, fmt.Errorf(\"exa API error: %s\", data.Error)\n\t}\n\n\tresults := make([]*types.WebSearchResult, 0, len(data.Results))\n\tfor _, item := range data.Results {\n\t\tif len(results) >= maxResults {\n\t\t\tbreak\n\t\t}\n\t\tsnippet := strings.TrimSpace(strings.Join(item.Highlights, \"\\n\"))\n\t\tcontent := truncateExaText(strings.TrimSpace(item.Text), maxExaContentRunes)\n\t\tif snippet == \"\" {\n\t\t\tsnippet = truncateExaText(content, 500)\n\t\t}\n\t\tresult := &types.WebSearchResult{Title: item.Title, URL: item.URL, Snippet: snippet, Content: content, Source: \"exa\"}\n\t\tif includeDate && item.PublishedDate != \"\" {\n\t\t\tif publishedAt, err := time.Parse(time.RFC3339, item.PublishedDate); err == nil {","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/infrastructure/web_search/exa.go#L93-L129","documentation":"The Exa provider failed to decode the HTTP response body into exaSearchResponse via json.Unmarshal. This means Exa returned a 2xx response whose body is not the expected JSON shape — commonly HTML (an error/interstitial page), an empty body, or a schema that changed.","triggerScenarios":"json.Unmarshal on the (already 2xx) Exa response body returns an error — invalid JSON syntax, unexpected types, or empty body despite HTTP 200.","commonSituations":"A proxy or captive portal returning HTML with status 200; Exa API version drift changing field types; truncation issues; network middleware injecting content.","solutions":["Log/capture the raw response body to see what was actually returned (often HTML from a proxy).","Verify no proxy or middleware is rewriting the response; bypass the proxy to test.","Pin/check the Exa API version and confirm your provider struct fields match the current response schema.","Add a content-type check before unmarshalling to fail with a clearer message."],"exampleFix":"null","handlingStrategy":"try-catch","validationCode":"// pre-flight: hit a cheap Exa endpoint and check Content-Type is application/json\nresp, _ := http.Get(baseURL)\nif !strings.Contains(resp.Header.Get(\"Content-Type\"), \"application/json\") {\n    return errors.New(\"exa response is not JSON — check proxy\")\n}","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"failed to unmarshal Exa response\") {\n    // capture raw body via logs and inspect; fall back to another provider\n    return fallbackProvider.Search(ctx, q, n, false)\n}","preventionTips":["Check response Content-Type before decoding","Keep the exaSearchResponse struct in sync with the Exa API docs","Pin the Exa API version if versioned endpoints are available","Log raw bodies on decode failure to spot HTML/proxy interstitials"],"tags":["json","unmarshal","web-search","exa","api"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}