Skip to content

fix(connections): give the key check a deadline and the executor's error mapping - #91

Merged
fajarhide merged 1 commit into
mainfrom
fix/validate-timeout
Aug 24, 2026
Merged

fix(connections): give the key check a deadline and the executor's error mapping#91
fajarhide merged 1 commit into
mainfrom
fix/validate-timeout

Conversation

@fajarhide

Copy link
Copy Markdown
Owner

Validating an api key was the one outbound call with no timeout and no rate-limit handling. A vendor that accepted the connection and never answered held an Express handler for undici's default, and a 429 came back as a 502 with no retry_after, so a client retried straight into the limit.

The cause was a second error mapping living beside the executor's, so I removed the second one rather than adding two arms to it. The arms readResponse already had are now a shared throwIfFailed, and validateKey routes through it. bodyFailure, the header rate-limit rule, 402, 429 with its retry hint, and the vendor's own reason text all arrive without being written twice. The two callers differ on one argument: a stored credential that stopped working is reauth_required, a key that arrived in the request being served is invalid_arguments.

The deadline is AbortSignal.timeout rather than the raced promise withTimeout uses (call-tool.ts:160), because this has to cancel the request rather than only stop waiting on it, and followRedirects spreads init so the signal survives every hop. Ten seconds rather than the thirty a tool call gets, since somebody is holding an HTTP request open on this one.

Closes #90

Evidence

The extraction changed no behaviour: 451 passed with no test edited before anything new was added. Four tests then cover what was missing, and two of the existing ones already guard the shared path.

Broken deliberately, signal dropped and the credential code swapped to the executor's:

 × rejects an api key when its validation request fails
   → expected GatewayError: demo rejected the credential to match object { code: 'invalid_arguments' }
 × gives the key check a deadline and cancels it rather than only waiting
   → expected undefined to be an instance of AbortSignal
   Tests  2 failed | 47 passed (49)

Restored, full suite:

 Test Files  42 passed (42)
      Tests  455 passed (455)

What I did not do

The timeout is asserted by checking the signal reaches the vendor and that an abort maps to upstream_timeout, not by letting a real ten second deadline elapse in the suite. No live vendor was called.

…ror mapping

Validating an api key was the one outbound call with no timeout and no
rate-limit handling. A vendor that accepted the connection and never answered
held an Express handler for undici's default, and a 429 came back as a 502 with
no retry_after, so a client retried straight into the limit.

The cause was a second error mapping living beside the executor's. The arms
readResponse already had are now a shared throwIfFailed, and validateKey routes
through it, so bodyFailure, the header rate-limit rule, 402, 429 with its retry
hint and the vendor's own reason all arrive without being written twice. The
two callers differ on one argument: a stored credential that stopped working is
reauth_required, and a key that arrived in the request being served is
invalid_arguments.

The deadline is AbortSignal.timeout rather than the raced promise withTimeout
uses, because this has to cancel the request instead of only stopping the wait,
and followRedirects spreads init so the signal survives every hop. Ten seconds
rather than the thirty a tool call gets: somebody is holding an HTTP request
open on this one.

Closes #90
@fajarhide fajarhide self-assigned this Aug 24, 2026
@fajarhide
fajarhide merged commit 9ba4d91 into main Aug 24, 2026
2 checks passed
@fajarhide
fajarhide deleted the fix/validate-timeout branch August 24, 2026 08:17
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.

The api key validation call has no timeout and no rate-limit handling, unlike every other outbound call

1 participant