Tencent/WeKnora · error

failed to unmarshal Zhipu response: %w

Error message

failed to unmarshal Zhipu response: %w

What it means

The Zhipu provider received an HTTP 200 response whose body is not valid JSON matching the zhipuSearchResponse schema. json.Unmarshal failed, so the provider wraps the decode error. This indicates the API returned something other than the expected search-result payload.

Source

Thrown at internal/infrastructure/web_search/zhipu.go:159

	logger.Infof(ctx, "[WebSearch][Zhipu] query=%q maxResults=%d engine=%s", preparedQuery, maxResults, p.searchEngine)
	resp, err := p.client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to execute Zhipu request: %w", err)
	}
	defer resp.Body.Close()

	respBody, err := readZhipuResponseBody(resp.Body)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode != http.StatusOK {
		return nil, zhipuHTTPError(resp.StatusCode, respBody)
	}

	var response zhipuSearchResponse
	if err := json.Unmarshal(respBody, &response); err != nil {
		return nil, fmt.Errorf("failed to unmarshal Zhipu response: %w", err)
	}
	if response.Error.Message != "" || response.Error.Code != "" {
		return nil, fmt.Errorf("Zhipu API error (%s): %s", response.Error.Code, response.Error.Message)
	}

	results := make([]*types.WebSearchResult, 0, len(response.SearchResult))
	for _, item := range response.SearchResult {
		if strings.TrimSpace(item.Title) == "" && strings.TrimSpace(item.Link) == "" {
			continue
		}
		result := &types.WebSearchResult{
			Title:   item.Title,
			URL:     item.Link,
			Snippet: item.Content,
			Source:  "zhipu",
		}
		if includeDate {
			if publishedAt, ok := parseZhipuDate(item.PublishDate); ok {

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Log the raw response body on this failure to see what was actually returned
  2. Verify you are targeting a current, supported Zhipu API endpoint/version
  3. Check for proxies or gateways rewriting the response body
  4. Add a content-type check before unmarshalling

Example fix

// before
respBody, err := readZhipuResponseBody(resp.Body)
// after
respBody, err := readZhipuResponseBody(resp.Body)
if err == nil && !json.Valid(respBody) {
    return nil, fmt.Errorf("non-JSON response: %.200s", respBody)
}
Defensive patterns

Strategy: try-catch

Validate before calling

if resp.StatusCode == 200 && !strings.Contains(resp.Header.Get("Content-Type"), "json") { return errors.New("unexpected content-type from zhipu") }

Type guard

func isUnmarshalError(err error) bool { var ue *json.UnmarshalTypeError; return errors.As(err, &ue) }

Try / catch

results, err := provider.Search(ctx, q)
if err != nil && strings.Contains(err.Error(), "failed to unmarshal Zhipu response") {
    log.WithError(err).Warn("zhipu returned non-JSON body; check proxy/API version")
}

Prevention

When it happens

Trigger: Calling Search() when Zhipu returns a 200 with an HTML error page, truncated body, or an unexpected JSON shape (schema change on the API side).

Common situations: A gateway/proxy intercepting the response and returning HTML; API version drift changing the response schema; a captive-portal or WAF returning a 200 with non-JSON content.

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/993de3e98610af5e. Report an issue: GitHub.