JuliusBrussee/caveman · info · DOMException
AbortError
AbortError
Error message
This operation was aborted
What it means
Inside sendThroughProxy, after the HTTPS CONNECT tunnel to the proxy succeeds and the destination request is about to be written, the promise chain re-checks the AbortSignal. If the caller's signal was aborted while the tunnel was opening, the socket is destroyed and an AbortError DOMException ('This operation was aborted') is thrown, which propagates to the caller through .catch(reject). This is fetch-spec-compliant cancellation behavior for proxied requests.
Source
Thrown at packages/cli/src/proxy-fetch.ts:241
// Plain HTTP is proxied in absolute form; no tunnel needed.
void finish(
{
host: proxy.hostname,
port: portOf(proxy),
method,
path: url.toString(),
headers: { ...headers, host: url.host, ...proxyAuthHeader(proxy) },
},
sendToProxy,
);
return;
}
openTunnel(url, proxy, signal)
.then((socket) => {
if (signal?.aborted) {
socket.destroy();
throw abortError();
}
return finish(
{
method,
path: `${url.pathname}${url.search}`,
headers: { ...headers, host: url.host },
// Node 26 rejects an IP literal as SNI servername; omit it for IP targets.
createConnection: () => tlsConnect({ socket, host: url.hostname, servername: isIP(url.hostname) ? undefined : url.hostname }),
},
httpsRequest,
);
})
.catch(reject);
});
}
async function bufferedRequestBody(request: Request): Promise<Uint8Array | null> {
if (["GET", "HEAD"].includes(request.method)) return null;View on GitHub (pinned to 5184b3d11a)
Solutions
- Don't abort before the timeout expires — increase the AbortSignal.timeout / controller deadline to account for proxy CONNECT overhead (proxy hops add a round trip).
- Catch AbortError distinctly and treat it as cancellation, not a server failure: check error.name === 'AbortError' before retrying.
- If the abort was unintentional (shared signal, leaked controller), fix the ownership of the signal so only the request's owner aborts it.
- If proxies are persistently too slow for your deadline, verify proxy reachability/credentials or bypass the proxy for that host (NO_PROXY / bypass rules).
Example fix
// before: tight timeout ignores proxy CONNECT latency
const res = await proxyFetch(url, { signal: AbortSignal.timeout(1000) });
// after: allow for the extra proxy hop, and handle cancellation explicitly
try {
const res = await proxyFetch(url, { signal: AbortSignal.timeout(10000) });
} catch (error) {
if (error instanceof Error && error.name === "AbortError") {
// caller canceled or timed out — don't surface as a server error
return null;
}
throw error;
} Defensive patterns
Strategy: try-catch
Validate before calling
// Before the call: give the tunnel extra budget and verify the signal is live.
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 15000); // includes CONNECT round trip
if (controller.signal.aborted) throw new Error("signal aborted before request started"); Type guard
function isAbortError(error: unknown): error is DOMException {
return error instanceof DOMException
? error.name === "AbortError"
: error instanceof Error && error.name === "AbortError";
} Try / catch
try {
const response = await proxyAwareFetch(url, { signal: controller.signal });
clearTimeout(timeout);
return response;
} catch (error) {
if (isAbortError(error)) {
return null; // caller cancellation or tunnel-stage timeout — not a server error
}
clearTimeout(timeout);
throw error;
} Prevention
- Budget timeouts for the extra proxy CONNECT round trip, not just origin latency.
- Only abort when you truly mean to cancel; don't share one controller across unrelated requests.
- Always branch on error.name === 'AbortError' before retry logic so cancellations aren't retried.
- If CONNECT tunnels routinely exceed deadlines, check proxy reachability/auth or add the host to bypass rules.
When it happens
Trigger: Calling the proxy-aware fetch (createProxyAwareFetch) for an https: URL routed through a proxy with an AbortSignal, then calling AbortController.abort() (or the signal's own timeout firing) while openTunnel is still negotiating the CONNECT; the abort lands between tunnel open and the `finish(...)` write, hitting the `signal?.aborted` check at line 239.
Common situations: AbortSignal.timeout() expiring because the proxy CONNECT was slow (latency to proxy, proxy auth delays, cold connections); a request watchdog or race in app code canceling the fetch; user navigation canceling in-flight requests; retry wrappers that abort a pending attempt.
Related errors
- binary download failed: ${error.message}
- device login polling failed: ${error instanceof Error ? erro
- caveman trial proxy did not become ready on ${host}:${port}
- unsupported proxy protocol: ${proxy.protocol}
- fetch failed: too many redirects
AI-assisted analysis of JuliusBrussee/caveman@5184b3d11a (2026-08-31).
Data as JSON: /api/errors/6bdad22af1dd8b5f.
Report an issue: GitHub.