Skip to content

Retry rate-limited searches and surface a keyless search provider - #199

Merged
plombeer31 merged 2 commits into
mainfrom
fix/search-429-backoff-and-key-warning
Aug 21, 2026
Merged

Retry rate-limited searches and surface a keyless search provider#199
plombeer31 merged 2 commits into
mainfrom
fix/search-429-backoff-and-key-warning

Conversation

@sosidudku1

Copy link
Copy Markdown
Collaborator

Fixes the largest single source of tool failure in the GAIA validation
campaign: 1341 Exa returned HTTP 429 errors, 44% of all 3039 tool
failures. Addresses suggestions 1 and 3 of #179.

Retry before falling through

A 429 threw straight out of the provider, and the orchestrator advances
its fallback chain on any throw. One transient rate limit therefore
downgraded the rest of the session to the weakest configured provider.

searchHttp now retries a 429 against the same provider (2 retries,
500 ms doubling) before returning it. A server Retry-After wins over the
local schedule; both clamp to 10 s so one hostile header cannot stall a
turn. The header rides the existing curl -w meta line via
%header{retry-after}; curl older than 7.83 emits the literal format
string, which is treated as absent. Retries are spent, not skipped, when a
limit is real — the fallback chain remains the backstop.

Name the degradation

web.search.provider defaults to exa, which runs keyless when
EXA_API_KEY is unset. The duckduckgo fallback then works as designed,
so nothing hard-fails and no error surfaces — the run just produces weaker
groundings than configured.

One stderr line at tool construction now names the provider, the unset
variable, and the fallback that will actually serve traffic. Once at
construction, not per search, so a long autonomous run does not drown in it.

On suggestion 2 (cacheTtlMinutes)

Not changed, deliberately. The cache is per-process, in-memory, capped at
256 entries, and keyed on the exact query string. A longer TTL neither
survives the per-task restarts a campaign does nor catches the near-miss
rephrasings that actually burn quota, while it would serve staler results
for time-sensitive lookups. A restart-surviving cache is the real fix and
is a separate piece of work.

Testing

24 new tests; npm run lint clean. Full suite: 4861 passed, 4 failed —
all 4 pre-existing on origin/main (fs-glob-real depends on files in
$HOME; llm-health-poller is a timing flake that passes in isolation).

🤖 Generated with Claude Code

sosidudku1 and others added 2 commits August 20, 2026 17:14
A single HTTP 429 used to throw straight out of the provider, and the
orchestrator advances its chain on any throw. One transient rate limit
therefore downgraded a whole session to the weakest configured provider.
A GAIA validation campaign logged 1341 keyless-Exa 429s, 44% of all tool
failures in the run.

searchHttp now retries a 429 against the same provider (2 retries, 500ms
doubling) before returning it. A server Retry-After wins over the local
schedule; both clamp to 10s so one hostile header cannot stall a turn.
The header rides the existing curl -w meta line via %header{retry-after},
read off the marker rather than a fixed field because a header value may
itself contain the separator. curl older than 7.83 emits the literal
format string, which is treated as absent.

Retries are spent, not skipped, when a limit is real: the fallback chain
remains the backstop once they are exhausted.

Refs #179

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
web.search.provider defaults to exa, whose MCP endpoint answers keyless
when EXA_API_KEY is unset. The duckduckgo fallback then works exactly as
designed, so nothing hard-fails and no error surfaces; the run just
produces weaker groundings than the operator configured. Silent
degradation is the worst of both worlds because it neither works well nor
says why.

Emit one stderr line at tool construction naming the provider, the unset
variable, and the fallback that will actually serve traffic. Warning once
at construction rather than per search keeps a long autonomous run from
drowning in it.

Also documents both mechanisms in AGENTS.md and corrects the module map,
which still described duckduckgo as the default provider.

Refs #179

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@sosidudku1
sosidudku1 force-pushed the fix/search-429-backoff-and-key-warning branch from 9b59167 to e5c4801 Compare August 20, 2026 14:44
@plombeer31
plombeer31 merged commit 6165aeb into main Aug 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants