Tencent/WeKnora · error

failed to marshal Exa request: %w

Error message

failed to marshal Exa request: %w

What it means

Wraps errors from json.Marshal when serializing the Exa search request payload (query, result count, contents options). With a plain struct of string/bool/int fields this is nearly impossible at runtime and signals a programming or struct-definition problem rather than an environmental one.

Source

Thrown at internal/infrastructure/web_search/exa.go:85

		return nil, fmt.Errorf("query is empty")
	}
	if maxResults <= 0 {
		maxResults = defaultExaResults
	}
	if maxResults > maxExaResults {
		maxResults = maxExaResults
	}

	bodyBytes, err := json.Marshal(exaSearchRequest{
		Query:      query,
		NumResults: maxResults,
		Contents: exaContents{
			Highlights: true,
			Text:       p.includeText,
		},
	})
	if err != nil {
		return nil, fmt.Errorf("failed to marshal Exa request: %w", err)
	}
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, p.baseURL, bytes.NewReader(bodyBytes))
	if err != nil {
		return nil, fmt.Errorf("failed to create Exa request: %w", err)
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("x-api-key", p.apiKey)

	logger.Infof(ctx, "[WebSearch][Exa] query=%q maxResults=%d url=%s", query, maxResults, p.baseURL)
	resp, err := p.client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to execute Exa request: %w", err)
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(io.LimitReader(resp.Body, maxExaResponseBytes))
	if err != nil {
		return nil, fmt.Errorf("failed to read Exa response: %w", err)
	}

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Review the request struct for unsupported field types or faulty custom MarshalJSON methods
  2. Revert recent changes to the exaRequest struct
  3. If truly impossible, treat as an internal invariant bug and alert rather than retry
  4. Check the unwrapped json.UnsupportedTypeError for the offending field

Example fix

// before
Contents: exaContents{ Channel: make(chan int) }, // unmarshalable field
// after
Contents: exaContents{ Highlights: true, Text: p.includeText },
Defensive patterns

Strategy: validation

Validate before calling

func validateExaRequestFields(r exaRequest) error {
    if r.NumResults < 0 {
        return errors.New("numResults must be non-negative")
    }
    return nil // current struct is JSON-safe; marshal errors indicate a struct change
}

Try / catch

results, err := provider.Search(ctx, query, 10, false)
if err != nil && strings.Contains(err.Error(), "failed to marshal Exa request") {
    return nil, fmt.Errorf("internal bug in exa request construction: %w", err)
}

Prevention

When it happens

Trigger: Calling Search when the exaRequest struct cannot be marshaled — practically only if the struct gains unsupported field types (e.g. chan, func, or a custom Marshaler that errors).

Common situations: After a code change adding a field with an unsupported type or a custom json.Marshaler that returns an error; essentially never in production with the current fixed schema.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


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