Skip to content

fix(common): bound AlchemyRestClient requests with timeout + AbortSignal - #2571

Open
hermiondev wants to merge 1 commit into
alchemyplatform:mainfrom
hermiondev:fix/rest-client-timeout
Open

hermiondev wants to merge 1 commit into
alchemyplatform:mainfrom
hermiondev:fix/rest-client-timeout

Conversation

@hermiondev

@hermiondev hermiondev commented Sep 27, 2026 •

Copy link
Copy Markdown

Problem

AlchemyRestClient.request() calls bare fetch() with no signal, and AlchemyRestClientParams exposes no timeout / AbortSignal option — consumers have no legitimate way to bound a request. If the upstream accepts the TCP connection but never responds (saturated LB, half-open keep-alive, captive middleware), the promise never settles.

Reproduced against the published @alchemy/common@5.2.7 dist and against main source with a local never-replying server:

build behavior
main / 5.2.7 dist (before) request unsettled past 15s → external watchdog kill (exit 124)
this fix, timeout: 3000 (after) FetchError: The operation was aborted due to timeout at 3002ms

The JSON-RPC sibling already has this protection — alchemy.ts wraps viem's http() transport, which applies timeout ?? 10_000 via an internal AbortSignal. The REST client is the unprotected sibling.

Fix

  • AlchemyRestClientParams gains optional timeout (default 10_000, matching the viem http transport default) and signal (takes precedence when provided).
  • request() applies this.signal ?? AbortSignal.timeout(this.timeout) to every fetch; aborts surface through the existing FetchError path — no new error type, no behavior change for healthy upstreams.

Verification

  • tsc --noEmit: no new errors vs clean main (5 pre-existing test-mock errors unchanged).
  • Live probe vs never-replying server: before = hang forever, after = clean FetchError at the configured timeout.
  • No existing consumers are affected (exports for this class are still commented out in index.ts; this hardens the surface before it graduates).

PR-Codex overview

This PR introduces enhancements to the AlchemyRestClient, adding support for request timeouts and abort signals to improve error handling and prevent indefinite hanging during requests.

Detailed summary

  • Added timeout property to specify request timeout in milliseconds.
  • Added signal property to allow an AbortSignal for request cancellation.
  • Updated the constructor to accept timeout and signal parameters.
  • Modified the request method to use the timeout and signal for aborting requests.

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

The bare fetch() in AlchemyRestClient.request() passed no signal and the
params type exposed no timeout/AbortSignal option, so a stalled upstream
(TCP accepted, never replies) left the promise unsettled forever - 12s
hang reproduced against the published @alchemy/common@5.2.7 dist with a
never-replying local server.

Add client-level timeout (default 10_000ms, matching the viem http
transport used by the JSON-RPC sibling) and optional AbortSignal; every
request is now bounded and rejects with the existing FetchError.
Before: hangs past 15s watchdog kill. After: FetchError at 3002ms on
timeout:3000 probe.

This branch has not been deployed

No deployments
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.

1 participant