JuliusBrussee/caveman · warning · DOMException
This operation was aborted
Error message
This operation was aborted
What it means
The fetch operation was aborted through its AbortSignal while a proxied HTTPS request was being set up: after the CONNECT tunnel opened, the signal was already aborted, so the tunnel socket is destroyed and a standard DOM-style AbortError ("This operation was aborted") is thrown instead of issuing the request. This mirrors the fetch spec behavior of rejecting with an abort error when a signal fires.
Solutions
- Expected behavior: handle the AbortError in your calling code (check `error.name === "AbortError"`) rather than treating it as a network failure.
- Increase or remove the timeout that fires the AbortController for slow proxied requests.
- Check proxy reachability/latency (HTTP_PROXY/HTTPS_PROXY env) — slow tunnels make aborts more likely.
- Abort only after the response has been consumed if you need the body.
Example fix
// before
const res = await fetch(url, { signal: controller.signal });
// after
try {
const res = await fetch(url, { signal: controller.signal });
} catch (e) {
if (e.name === "AbortError") return null; // expected cancellation
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
if (signal?.aborted) return; // skip the call entirely before fetching
Type guard
function isAbortError(e: unknown): e is DOMException {
return e instanceof Error && e.name === "AbortError";
} Try / catch
try {
const res = await proxyAwareFetch(url, { signal: controller.signal });
} catch (e) {
if (isAbortError(e)) return null; // expected cancellation, not a network failure
throw e;
} Prevention
- Set request timeouts generously for proxied HTTPS (CONNECT adds a handshake hop).
- Check signal.aborted before issuing each fetch.
- Verify HTTP_PROXY/HTTPS_PROXY reachability; slow proxies inflate tunnel time.
- Don't abort while a response body is still being consumed.
When it happens
Trigger: Calling the proxy-aware fetch (via `caveman` CLI network paths) with `init.signal` and aborting the controller while the HTTPS CONNECT tunnel to the proxy is opening, or passing an already-aborted signal that races the tunnel `.then` handler; the abort listener attached to the outbound request also destroys the socket with this error.
Common situations: CLI commands with their own request timeouts aborting in-flight proxied calls, users pressing Ctrl-C, or an upstream caller cancelling a request whose proxy tunnel handshake is slow (common with slow/blocked corporate proxies).
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
Related errors
- AbortError
- binary download failed
- bounded_response_unavailable
- cave response was not valid JSON
- caveman trial proxy did not become ready on
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/5eb32407c991f34c.
Report an issue: GitHub.
Appendix: 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 3ee70a1026)