{"record":{"id":"9451a3883d7106ef","repo":"Tencent/WeKnora","slug":"failed-to-unmarshal-response-w-9451a3","errorCode":null,"errorMessage":"failed to unmarshal response: %w","messagePattern":"failed to unmarshal response: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/infrastructure/web_search/ollama.go","lineNumber":130,"sourceCode":"\tresp, err := p.client.Do(req)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer resp.Body.Close()\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif resp.StatusCode != http.StatusOK {\n\t\tlogger.Warnf(ctx, \"[WebSearch][Ollama] API returned status %d: %s\", resp.StatusCode, string(body))\n\t\treturn nil, fmt.Errorf(\"ollama API returned status %d: %s\", resp.StatusCode, string(body))\n\t}\n\n\tvar respData ollamaSearchResponse\n\tif err := json.Unmarshal(body, &respData); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to unmarshal response: %w\", err)\n\t}\n\n\tresults := make([]*types.WebSearchResult, 0, len(respData.Results))\n\tfor _, item := range respData.Results {\n\t\tresults = append(results, &types.WebSearchResult{\n\t\t\tTitle:   item.Title,\n\t\t\tURL:     item.URL,\n\t\t\tSnippet: item.Snippet,\n\t\t\tContent: item.Content,\n\t\t\tSource:  \"ollama\",\n\t\t})\n\t}\n\treturn results, nil\n}\n\n// ollamaSearchResponse defines the response structure for Ollama web search API.\ntype ollamaSearchResponse struct {\n\tResults []ollamaSearchResult `json:\"results\"`","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/infrastructure/web_search/ollama.go#L112-L148","documentation":"The Ollama provider received a 200 response but the body could not be parsed into ollamaSearchResponse. fmt.Errorf with %w wraps the underlying json.Unmarshal error, so the exact JSON syntax/type mismatch is preserved in the chain.","triggerScenarios":"Search -> doSearch receives HTTP 200 but the body is not the expected JSON shape: HTML from a misconfigured proxy, truncated response, or Ollama returning a schema that changed.","commonSituations":"A reverse proxy intercepts requests and returns HTML, an old/new Ollama version changes response fields, network middleware corrupts the payload, or the wrong port serves a different service.","solutions":["Log or print the raw body to see what was actually returned","Verify the endpoint really is the Ollama search API (no proxy/HTML response)","Check Ollama version compatibility with the expected response schema","Update the client struct to match the actual API schema if the API changed"],"exampleFix":"// before\n// struct expects {\"results\":[{\"title\":...}]}\n// after: inspect body first\nif !json.Valid(body) { return nil, fmt.Errorf(\"non-JSON response: %.200s\", body) }","handlingStrategy":"try-catch","validationCode":"// sanity-check endpoint returns JSON before use\nresp, err := http.Get(baseURL + healthPath)\nif err == nil {\n    ct := resp.Header.Get(\"Content-Type\")\n    if !strings.Contains(ct, \"application/json\") {\n        return fmt.Errorf(\"endpoint does not return JSON: %s\", ct)\n    }\n}","typeGuard":"func isUnmarshalError(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"failed to unmarshal response\")\n}","tryCatchPattern":"results, err := provider.Search(ctx, q)\nif err != nil {\n    if isUnmarshalError(err) {\n        log.Printf(\"Ollama returned non-JSON/changed schema: %v\", err)\n        return fallbackSearch(ctx, q)\n    }\n    return err\n}","preventionTips":["Ensure no proxy returns HTML for API routes","Test the response schema after any Ollama version upgrade","Check Content-Type before unmarshalling when possible","Keep the response struct updated with the deployed Ollama API version"],"tags":["json","unmarshal","ollama","http"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}