Summary
Add a provider fallback chain to web_search so that when the primary provider's free quota is exhausted (HTTP 429/402), the tool automatically retries with the next available provider instead of failing entirely.
Problem to solve
Currently web_search binds to a single provider at startup (resolveSearchProvider()) and has no fallback mechanism. When the chosen provider's free quota is exhausted (e.g., Brave's 2,000 requests/month limit), the tool throws an error and the agent loses web search capability entirely for the rest of the billing period.
Many users configure multiple provider API keys (e.g., BRAVE_API_KEY + GEMINI_API_KEY + KIMI_API_KEY) but can only use one at a time. The existing auto-detection logic picks the highest-priority provider with an available key, but never revisits that decision at runtime. Free tiers across providers add up to significant capacity (Brave 2k/mo + Gemini 5k/mo + Kimi free tier), but none of it is utilized when the primary provider is down or rate-limited.
Proposed solution
Configuration: Add an optional providers array for ordered fallback:
{
tools: {
web: {
search: {
// Ordered fallback chain
providers: ["brave", "gemini", "kimi"],
// Existing single-provider config still works (no fallback)
// provider: "brave",
},
},
},
}
Runtime behavior:
- On each
web_search call, try the primary provider
- If it returns a quota/rate-limit error (429, 402, or known quota error patterns), catch it and retry with the next provider in the chain
- If all providers are exhausted, return the last error
- Log which provider was used via
logVerbose
Schema handling: Use the union of common parameters (query, count, country, language, freshness, date_after, date_before) for the tool schema. Provider-specific params are silently ignored when falling back to a provider that does not support them.
Backward compatibility: Existing single provider: "brave" config continues to work unchanged (no fallback). The providers: [...] array is opt-in. Auto-detection priority order remains the same when no explicit config is set.
Alternatives considered
- Manual provider switching: Users can change config when a provider is exhausted, but this requires manual intervention and gateway restart — impractical for automated agents.
- Single "best" provider: Just use Gemini (highest free tier) — but this doesn't maximize free capacity across providers, and some users prefer Brave's structured results over Gemini's AI-synthesized format for certain queries.
- SearXNG self-hosted: Eliminates quota issues entirely, but requires server infrastructure and is a separate feature request.
Impact
- Affected users: All users relying on free-tier search API keys, especially hobbyists and small teams
- Severity: Medium — blocks web search entirely once quota is hit
- Frequency: Monthly (when free quotas reset), more frequent for active users
- Consequence: Agent loses web search capability for days/weeks until quota resets; users must manually reconfigure to a different provider
Evidence/examples
- Current provider resolution:
src/agents/tools/web-search.ts resolveSearchProvider() — picks one provider, no retry
- Error handling: all providers throw on HTTP errors with no catch-and-retry path
- Precedent in codebase:
tools.media.models already supports ordered fallback lists for media understanding
- Free tier capacities: Brave (2k/mo), Gemini grounding (5k/mo), Tavily (1k/mo), Serper (2.5k one-time) — combined these cover most personal use cases
Additional information
- Must remain backward-compatible with existing single-provider config
- Consider exposing a
provider field in the tool result (already present) so agents/users can see which provider served each query
- Long-term: could add Tavily and SearXNG as additional providers to further expand the fallback pool
Summary
Add a provider fallback chain to
web_searchso that when the primary provider's free quota is exhausted (HTTP 429/402), the tool automatically retries with the next available provider instead of failing entirely.Problem to solve
Currently
web_searchbinds to a single provider at startup (resolveSearchProvider()) and has no fallback mechanism. When the chosen provider's free quota is exhausted (e.g., Brave's 2,000 requests/month limit), the tool throws an error and the agent loses web search capability entirely for the rest of the billing period.Many users configure multiple provider API keys (e.g.,
BRAVE_API_KEY+GEMINI_API_KEY+KIMI_API_KEY) but can only use one at a time. The existing auto-detection logic picks the highest-priority provider with an available key, but never revisits that decision at runtime. Free tiers across providers add up to significant capacity (Brave 2k/mo + Gemini 5k/mo + Kimi free tier), but none of it is utilized when the primary provider is down or rate-limited.Proposed solution
Configuration: Add an optional
providersarray for ordered fallback:Runtime behavior:
web_searchcall, try the primary providerlogVerboseSchema handling: Use the union of common parameters (
query,count,country,language,freshness,date_after,date_before) for the tool schema. Provider-specific params are silently ignored when falling back to a provider that does not support them.Backward compatibility: Existing single
provider: "brave"config continues to work unchanged (no fallback). Theproviders: [...]array is opt-in. Auto-detection priority order remains the same when no explicit config is set.Alternatives considered
Impact
Evidence/examples
src/agents/tools/web-search.tsresolveSearchProvider()— picks one provider, no retrytools.media.modelsalready supports ordered fallback lists for media understandingAdditional information
providerfield in the tool result (already present) so agents/users can see which provider served each query