From b7f095a0294699ce0720ea90d4530885a3f73921 Mon Sep 17 00:00:00 2001 From: Arda TANRIKULU Date: Tue, 22 Jul 2025 15:44:52 +0300 Subject: [PATCH] fix(executor-http): avoid shared `AbortController` in Cloudflare Workers --- .changeset/lovely-coins-dream.md | 6 ++++++ packages/executors/http/src/index.ts | 14 ++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 .changeset/lovely-coins-dream.md diff --git a/.changeset/lovely-coins-dream.md b/.changeset/lovely-coins-dream.md new file mode 100644 index 000000000..5abf0c05f --- /dev/null +++ b/.changeset/lovely-coins-dream.md @@ -0,0 +1,6 @@ +--- +'@graphql-tools/executor-http': patch +--- + +Avoid shared AbortController instance on CloudflareWorkers because it gives `Cannot perform I/O on behalf of a different request.` error. +This change ensures that the AbortController is only created when not running in a Cloudflare Workers environment. \ No newline at end of file diff --git a/packages/executors/http/src/index.ts b/packages/executors/http/src/index.ts index 9105def32..7f589734d 100644 --- a/packages/executors/http/src/index.ts +++ b/packages/executors/http/src/index.ts @@ -163,8 +163,11 @@ export function buildHTTPExecutor( request: ExecutionRequest, excludeQuery?: boolean, ) => { - disposeCtrl ||= new AbortController(); - if (disposeCtrl.signal.aborted) { + // @ts-expect-error Cloudflare Workers doesn't like the shared AbortController + if (!request.cf) { + disposeCtrl ||= new AbortController(); + } + if (disposeCtrl?.signal.aborted) { return createResultForAbort(disposeCtrl.signal.reason); } const fetchFn = request.extensions?.fetch ?? options?.fetch ?? defaultFetch; @@ -214,7 +217,10 @@ export function buildHTTPExecutor( request.extensions = restExtensions; } - const signals = [disposeCtrl.signal]; + const signals = []; + if (disposeCtrl?.signal) { + signals.push(disposeCtrl.signal); + } const signalFromRequest = request.signal || request.info?.signal; if (signalFromRequest) { if (signalFromRequest.aborted) { @@ -229,7 +235,7 @@ export function buildHTTPExecutor( signals.push(subscriptionCtrl.signal); } - const signal = abortSignalAny(signals); + const signal = signals.length ? abortSignalAny(signals) : undefined; const upstreamErrorExtensions: UpstreamErrorExtensions = { request: {