Tencent/WeKnora · error

tavily API returned status %d: %s

Error message

tavily API returned status %d: %s

What it means

The Tavily API responded with a non-200 HTTP status. The provider logs the status and body via Warnf, then returns this error containing both. This is a server-side rejection (auth, rate limit, bad endpoint) rather than a client-side network failure.

Source

Thrown at internal/infrastructure/web_search/tavily.go:93

		return nil, fmt.Errorf("failed to marshal request: %w", err)
	}

	req, err := http.NewRequestWithContext(ctx, "POST", p.baseURL, bytes.NewReader(bodyBytes))
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}
	req.Header.Set("Content-Type", "application/json")

	resp, err := p.client.Do(req)
	if err != nil {
		return nil, fmt.Errorf("failed to execute request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		respBody, _ := io.ReadAll(resp.Body)
		logger.Warnf(ctx, "[WebSearch][Tavily] API returned status %d: %s", resp.StatusCode, string(respBody))
		return nil, fmt.Errorf("tavily API returned status %d: %s", resp.StatusCode, string(respBody))
	}

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read response: %w", err)
	}

	var respData tavilySearchResponse
	if err := json.Unmarshal(respBody, &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 {
		result := &types.WebSearchResult{
			Title:   item.Title,
			URL:     item.URL,
			Snippet: item.Content,

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Read the status code and body from the error message to identify the cause (401 vs 429 vs 5xx).
  2. Verify the Tavily API key is valid and active; regenerate it in the Tavily dashboard if needed.
  3. For 429, implement rate limiting or retry with exponential backoff respecting Retry-After.
  4. For 5xx, retry later and check the Tavily status page for outages.
Defensive patterns

Strategy: try-catch

Validate before calling

// validate config before constructing the provider
if os.Getenv("TAVILY_API_KEY") == "" { return errors.New("TAVILY_API_KEY not set") }

Type guard

func isHTTPStatusErr(err error) (code int, body string, ok bool) {
    m := regexp.MustCompile(`tavily API returned status (\d+): (.*)`).FindStringSubmatch(err.Error())
    if m == nil { return 0, "", false }
    code, _ = strconv.Atoi(m[1])
    return code, m[2], true
}

Try / catch

results, err := provider.Search(ctx, q, 5, false)
if err != nil {
    if code, body, ok := isHTTPStatusErr(err); ok {
        switch {
        case code == 401: return fmt.Errorf("invalid tavily API key: %s", body)
        case code == 429: return retryAfterBackoff(ctx)
        case code >= 500: return retryWithBackoff(ctx)
        }
    }
    return err
}

Prevention

When it happens

Trigger: Calling provider.Search and receiving HTTP 401 (invalid API key), 400 (malformed request body), 429 (rate limit), or 5xx from the Tavily API.

Common situations: Expired or wrong TAVILY_API_KEY, exceeding plan quota/rate limits, Tavily API outages or breaking API changes, or account suspension.

Related errors


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