Tencent/WeKnora · error

failed to unmarshal response: %w

Error message

failed to unmarshal response: %w

What it means

json.Unmarshal could not decode the Bing response body into bingSearchResponse, so doSearch wraps the underlying error with this message. It means the body is not valid JSON or its shape doesn't match the expected struct.

Source

Thrown at internal/infrastructure/web_search/bing.go:116

	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][Bing] API returned status %d: %s", resp.StatusCode, string(body))
		return nil, fmt.Errorf("bing API returned status %d: %s", resp.StatusCode, string(body))
	}

	var respData bingSearchResponse
	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.WebPages.Value))
	for _, item := range respData.WebPages.Value {
		results = append(results, &types.WebSearchResult{
			Title:       item.Name,
			URL:         item.URL,
			Snippet:     item.Snippet,
			Source:      "bing",
			PublishedAt: &item.DateLastCrawled,
		})
	}
	return results, nil
}

// bingSearchResponse defines the response structure for Bing search API.
type bingSearchResponse struct {
	Type         string `json:"_type"`
	QueryContext struct {

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Log or inspect the raw body (the provider already has it in doSearch) to see what was actually returned.
  2. Confirm no corporate proxy or WAF is intercepting the request (headers, TLS MITM).
  3. Verify the Bing API version and that the endpoint still matches the struct definition (bingSearchResponse).
  4. Ensure the HTTP client is not double-decompressing or skipping gzip responses.

Example fix

// before
var respData bingSearchResponse
if err := json.Unmarshal(body, &respData); err != nil { ... }
// after
if !json.Valid(body) {
    return nil, fmt.Errorf("non-JSON response body: %.200s", body)
}
var respData bingSearchResponse
if err := json.Unmarshal(body, &respData); err != nil { ... }
Defensive patterns

Strategy: try-catch

Type guard

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

Try / catch

results, err := provider.Search(ctx, query, 10, false)
if err != nil && strings.Contains(err.Error(), "failed to unmarshal response") {
    // log raw body via debug logging, fall back to another provider
    return fallbackProvider.Search(ctx, query, 10, false)
}

Prevention

When it happens

Trigger: Bing returns HTML/XML error pages, a proxy/CAPTCHA response, or JSON missing the expected webPages.value structure; body was truncated by a network fault.

Common situations: Hitting a captive proxy that injects HTML; Bing changing/retiring its response schema; response gzip/compression not handled; a 200 response from a gateway instead of the real API.

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/93deaa04696df68f. Report an issue: GitHub.