schollz/croc · info · DOMException
AbortError
AbortError
Error message
Transfer cancelled
What it means
checkAbort() throws this DOMException (name AbortError) whenever the supplied AbortSignal is already aborted. It is sprinkled at loop boundaries of prepareStoredFiles hashing, chunk upload, chunk download, and putCiphertext retries so cancellation takes effect promptly and is uniformly represented as a standard AbortError.
Source
Thrown at web/src/protocol/stored.ts:96
totalSize: number;
};
type CreatedStoredUpload = {
share: StoredShare;
uploadToken: string;
};
class StoredHTTPError extends Error {
constructor(
message: string,
readonly status: number,
) {
super(message);
}
}
function checkAbort(signal?: AbortSignal) {
if (signal?.aborted) throw new DOMException("Transfer cancelled", "AbortError");
}
function base64URL(bytes: Uint8Array) {
let binary = "";
for (let offset = 0; offset < bytes.byteLength; offset += 0x8000) {
binary += String.fromCharCode(...bytes.subarray(offset, offset + 0x8000));
}
return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
}
function fromBase64URL(value: string) {
const normalized = value.replaceAll("-", "+").replaceAll("_", "/");
const padded = normalized + "=".repeat((4 - (normalized.length % 4)) % 4);
const binary = atob(padded);
const bytes = new Uint8Array(binary.length);
for (let index = 0; index < binary.length; index += 1) {
bytes[index] = binary.charCodeAt(index);
}View on GitHub (pinned to e25f1bdc04)
Solutions
- Treat DOMException name === "AbortError" as a clean cancel: stop the flow, call sink.abort()/revoke if needed, and show a 'cancelled' state
- Create a fresh AbortController for each new operation instead of reusing an aborted one
- If the abort was unexpected, find who owns the controller and why it fired (component unmount, timeout, navigation)
Example fix
// before
const result = await uploadStoredFiles({ ..., signal: controller.signal }); // reuses aborted controller
// after
controller = new AbortController(); // fresh controller per operation
const result = await uploadStoredFiles({ ..., signal: controller.signal }); Defensive patterns
Strategy: try-catch
Validate before calling
const assertNotAborted = (signal?: AbortSignal) => { if (signal?.aborted) throw new DOMException("Transfer cancelled", "AbortError"); }; Type guard
const isAbort = (e: unknown): boolean => e instanceof DOMException && e.name === "AbortError";
Try / catch
try { await uploadStoredFiles({ files, settings, signal }); } catch (e) { if (e instanceof DOMException && e.name === "AbortError") { setStatus("Cancelled"); return; } throw e; } Prevention
- Create a new AbortController per operation; never reuse an aborted one
- Abort controllers on user action or unmount, and always handle the resulting AbortError explicitly
When it happens
Trigger: Passing an aborted signal to prepareStoredFiles, uploadStoredFiles, receiveStoredTransfer, or any putCiphertext/authorizedFetch call; or the signal aborting mid-loop and the next checkAbort firing before fetch's own signal handling would.
Common situations: User clicks a Cancel button wired to an AbortController; a React component unmounts and aborts its controller but an await chain still runs; UI code that reuses one controller for multiple sequential operations after aborting it earlier.
Related errors
AI-assisted analysis of schollz/croc@e25f1bdc04 (2026-08-15).
Data as JSON: /api/errors/ecc1963d09601106.
Report an issue: GitHub.