Tencent/WeKnora · error

failed to unmarshal response: %w

Error message

failed to unmarshal response: %w

What it means

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.

Source

Thrown at internal/infrastructure/web_search/ollama.go:130

	resp, err := p.client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != http.StatusOK {
		logger.Warnf(ctx, "[WebSearch][Ollama] API returned status %d: %s", resp.StatusCode, string(body))
		return nil, fmt.Errorf("ollama API returned status %d: %s", resp.StatusCode, string(body))
	}

	var respData ollamaSearchResponse
	if err := json.Unmarshal(body, &respData); err != nil {
		return nil, fmt.Errorf("failed to unmarshal response: %w", err)
	}

	results := make([]*types.WebSearchResult, 0, len(respData.Results))
	for _, item := range respData.Results {
		results = append(results, &types.WebSearchResult{
			Title:   item.Title,
			URL:     item.URL,
			Snippet: item.Snippet,
			Content: item.Content,
			Source:  "ollama",
		})
	}
	return results, nil
}

// ollamaSearchResponse defines the response structure for Ollama web search API.
type ollamaSearchResponse struct {
	Results []ollamaSearchResult `json:"results"`

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Log or print the raw body to see what was actually returned
  2. Verify the endpoint really is the Ollama search API (no proxy/HTML response)
  3. Check Ollama version compatibility with the expected response schema
  4. Update the client struct to match the actual API schema if the API changed

Example fix

// before
// struct expects {"results":[{"title":...}]}
// after: inspect body first
if !json.Valid(body) { return nil, fmt.Errorf("non-JSON response: %.200s", body) }
Defensive patterns

Strategy: try-catch

Validate before calling

// sanity-check endpoint returns JSON before use
resp, err := http.Get(baseURL + healthPath)
if err == nil {
    ct := resp.Header.Get("Content-Type")
    if !strings.Contains(ct, "application/json") {
        return fmt.Errorf("endpoint does not return JSON: %s", ct)
    }
}

Type guard

func isUnmarshalError(err error) bool {
    return err != nil && strings.Contains(err.Error(), "failed to unmarshal response")
}

Try / catch

results, err := provider.Search(ctx, q)
if err != nil {
    if isUnmarshalError(err) {
        log.Printf("Ollama returned non-JSON/changed schema: %v", err)
        return fallbackSearch(ctx, q)
    }
    return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/9451a3883d7106ef. Report an issue: GitHub.