fabricjs/fabric.js · warning · SignalAbortedError
loadImage
Error message
loadImage
What it means
SignalAbortedError thrown by loadImage when the options.signal is already aborted before the image load starts (or after the resourceValidator async check completes). It is the standard AbortSignal integration: aborting an enliven/load operation should cancel pending image fetches, and fabric surfaces that as a distinct error type rather than a generic failure.
Source
Thrown at packages/core/src/util/misc/objectEnlive.ts:44
* @param {LoadImageOptions} [options] image loading options
* @returns {Promise<HTMLImageElement>} the loaded image.
*/
export const loadImage = (
url: string,
{ signal, crossOrigin = null, resourceValidator }: LoadImageOptions = {},
): Promise<HTMLImageElement> => {
if (signal && signal.aborted) {
return Promise.reject(new SignalAbortedError('loadImage'));
}
if (url && resourceValidator) {
return Promise.resolve()
.then(() => resourceValidator(url))
.then((isAllowed) => {
if (!isAllowed) {
throw new FabricError(`Resource '${url}' is not allowed`);
}
if (signal && signal.aborted) {
throw new SignalAbortedError('loadImage');
}
return loadImage(url, { signal, crossOrigin });
});
}
return new Promise<HTMLImageElement>(function (resolve, reject) {
if (signal && signal.aborted) {
return reject(new SignalAbortedError('loadImage'));
}
const img = createImage();
let abort: EventListenerOrEventListenerObject;
if (signal) {
abort = function (err: Event) {
img.src = '';
reject(err);
};
signal.addEventListener('abort', abort, { once: true });
}
const done = function () {View on GitHub (pinned to 2bd4992cab)
Solutions
- Check signal.aborted before starting the load and skip it if aborted
- Ensure the AbortController isn't aborted prematurely (e.g. abort on unmount only when the load is truly obsolete)
- Catch SignalAbortedError specifically and treat it as a cancel, not a failure (skip UI error states)
- Create a fresh controller per load instead of reusing an aborted one
Example fix
// before
const img = await fabric.loadImage(url, { signal: controller.signal }); // throws SignalAbortedError('loadImage')
// after
if (!controller.signal.aborted) {
const img = await fabric.loadImage(url, { signal: controller.signal });
} Defensive patterns
Strategy: try-catch
Validate before calling
if (!controller.signal.aborted) {
img = await fabric.loadImage(url, { signal: controller.signal });
} Type guard
const isAborted = (signal?: AbortSignal): boolean => !!signal?.aborted;
Try / catch
try {
await fabric.loadImage(url, { signal: controller.signal });
} catch (e) {
if (e.name === 'SignalAbortedError' || controller.signal.aborted) {
// cancelled: silently return, do not surface as an error
} else throw e;
} Prevention
- Check signal.aborted before kicking off loads
- Use a fresh AbortController per load
- Abort only when the result is truly obsolete
- Handle SignalAbortedError as cancellation, not failure
When it happens
Trigger: Calling fabric.loadImage(url, { signal }) with an already-aborted AbortController's signal, or the controller being aborted while the resourceValidator promise resolves. Also occurs during canvas.loadFromJSON when the caller aborts mid-enliven.
Common situations: React components that abort a controller on unmount and then a late-loading image starts with the dead signal; race conditions where cleanup runs before load; passing a shared controller's signal that another operation already aborted.
Related errors
AI-assisted analysis of fabricjs/fabric.js@2bd4992cab (2026-08-28).
Data as JSON: /api/errors/d7ea3ae69f895969.
Report an issue: GitHub.