denoland/deno · error · DOMException
AbortError
AbortError
Error message
The lock request was aborted
What it means
When a LockManager.request() call has an AbortSignal and the request is queued (the lock is held elsewhere), aborting the signal rejects the request's promise. If the application set signal.reason it is re-thrown as-is; otherwise Deno throws a DOMException named AbortError with this message from ext/web/locks.js. The listener is removed and no lock is held — the callback never runs.
Source
Thrown at ext/web/locks.js:202
if (options.signal) {
const onAbort = () => op_lock_manager_cancel(rid);
options.signal.addEventListener("abort", onAbort, { once: true });
try {
heldRid = await op_lock_manager_await_lock(rid);
} finally {
options.signal.removeEventListener("abort", onAbort);
}
if (heldRid == null) {
throw options.signal.reason ??
new DOMException(
"The lock request was aborted",
"AbortError",
);
}
} else {
heldRid = await op_lock_manager_await_lock(rid);
if (heldRid == null) {
throw new DOMException(
"The lock request was aborted",
"AbortError",
);
}
}
}
try {
const lock = webidl.createBranded(Lock);
lock[_name] = name;
lock[_mode] = options.mode;
// Run the callback while concurrently watching for the lock being
// stolen. Awaiting the steal op also keeps the event loop alive for as
// long as the lock is held.
const callbackPromise = (async () => await callback(lock))();
const stolen = await SafePromiseRace([
op_lock_manager_await_steal(heldRid),
PromisePrototypeThen(callbackPromise, () => false),View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Catch the rejection around the request() call and treat AbortError as 'did not acquire' — skip or retry the work.
- Distinguish causes: if you set controller.abort(reason), compare e === reason before falling back to DOMException handling.
- For timeouts, prefer AbortSignal.timeout(ms) and catch AbortError explicitly.
- If aborts are unexpected, investigate the long lock holder — that is the real cause, not the abort.
Example fix
// before
const p = navigator.locks.request("r", { signal: ctrl.signal }, cb);
await p; // unhandled AbortError when aborted
// after
try {
await navigator.locks.request("r", { signal: ctrl.signal }, cb);
} catch (e) {
if (e.name === "AbortError") return; // acquisition cancelled
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
if (opts.signal?.aborted) {
return; // nothing to request
}
await navigator.locks.request(n, opts, cb); Type guard
function isAbortError(e) {
return e instanceof DOMException && e.name === "AbortError";
} Try / catch
try {
await navigator.locks.request(n, { signal: ctrl.signal }, cb);
} catch (e) {
if (e === ctrl.signal.reason || (e instanceof DOMException && e.name === "AbortError")) return; // cancelled while queued
throw e;
} Prevention
- Always await request() inside try/catch when a signal is attached.
- Use AbortSignal.timeout(ms) for acquisition deadlines and handle AbortError explicitly.
- Set controller.abort(reason) to distinguish intentional cancellation from generic aborts.
When it happens
Trigger: Aborting an AbortController while request() is queued behind another holder; a timeout via setTimeout(() => ctrl.abort(), 5000) racing lock acquisition; server shutdown aborting in-flight lock requests.
Common situations: Adding request timeouts to lock acquisition so slow holders cannot stall workers; graceful-shutdown paths that abort pending requests; racing a lock against user cancellation in a web UI.
Related errors
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/f3e88f0d3be22476.
Report an issue: GitHub.