Tencent/WeKnora · error
exa API returned status %d: %s
Error message
exa API returned status %d: %s
What it means
The Exa web-search provider wraps its Search HTTP call and returns this error whenever the Exa API responds with a non-2xx HTTP status. The provider reads the response body (bounded by maxExaResponseBytes), logs it at warn level, and surfaces both the status code and body so the caller can see why Exa rejected the request. It is a deliberate fail-fast for upstream API failures rather than a client-side bug.
Source
Thrown at internal/infrastructure/web_search/exa.go:106
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)
}
if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
logger.Warnf(ctx, "[WebSearch][Exa] API returned status %d: %s", resp.StatusCode, string(body))
return nil, fmt.Errorf("exa API returned status %d: %s", resp.StatusCode, string(body))
}
var data exaSearchResponse
if err := json.Unmarshal(body, &data); err != nil {
return nil, fmt.Errorf("failed to unmarshal Exa response: %w", err)
}
if data.Error != "" {
return nil, fmt.Errorf("exa API error: %s", data.Error)
}
results := make([]*types.WebSearchResult, 0, len(data.Results))
for _, item := range data.Results {
if len(results) >= maxResults {
break
}
snippet := strings.TrimSpace(strings.Join(item.Highlights, "\n"))
content := truncateExaText(strings.TrimSpace(item.Text), maxExaContentRunes)
if snippet == "" {View on GitHub (pinned to 988cbb0330)
Solutions
- Read the status code and body in the error message: 401/403 means fix the API key, 429 means back off and retry with delay, 5xx means retry later or check Exa status.
- Verify the Exa API key configured for the provider is valid and active.
- Add retry with exponential backoff for transient 429/5xx statuses.
- Check https://status.exa.ai or Exa changelogs if 4xx/5xx persists with a valid key.
Example fix
// before
results, err := exaProvider.Search(ctx, query, 10, false)
if err != nil { return err }
// after
results, err := exaProvider.Search(ctx, query, 10, false)
if err != nil {
var respErr *fmt.Errorf
if strings.Contains(err.Error(), "status 429") {
time.Sleep(2 * time.Second)
results, err = exaProvider.Search(ctx, query, 10, false)
}
if err != nil { return fmt.Errorf("web search failed: %w", err) }
} Defensive patterns
Strategy: retry
Validate before calling
if exaAPIKey == "" { return errors.New("exa API key missing — request will 401") } Try / catch
results, err := provider.Search(ctx, q, n, false)
if err != nil {
if strings.Contains(err.Error(), "status 429") || strings.Contains(err.Error(), "status 5") {
// retry with exponential backoff
}
return fmt.Errorf("exa search failed: %w", err)
} Prevention
- Rotate and verify API keys before deployment
- Wrap search calls with retry+backoff for 429/5xx
- Monitor Exa quota usage and alert before exhaustion
- Log full status and body on upstream failure for fast triage
When it happens
Trigger: Calling Search on the Exa provider when the HTTP response status is outside [200, 300) — e.g. 400 bad request body, 401/403 invalid or missing API key, 429 rate limit, 5xx Exa outage.
Common situations: Expired or wrong EXA API key; exceeding Exa's rate quota; malformed query parameters; Exa having a temporary outage or deploying a breaking API change; corporate proxy interfering with TLS.
Related errors
- bing API returned status %d: %s
- failed to unmarshal Exa response: %w
- exa API error: %s
- keenable API returned status %d: %s
- API key is required for Exa provider
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/38170c09489ed2c7.
Report an issue: GitHub.