# Troubleshooting

> Fabric.js troubleshooting for editors: lost custom properties after loadFromJSON, UNKNOWN_OBJECT_TYPE, a tainted canvas on export, undo that misses changes.

Source: https://fabricjs-document-engine.jscrate.dev/docs/production/troubleshooting
Last updated: 2026-09-28

This Fabric.js troubleshooting guide covers the problems people hit most, in the order they usually appear.

## Undo does not notice my change

Fabric.js only fires events for pointer interaction, adding and removing objects, and text editing. Changes your code makes with `object.set()` or `canvas.bringObjectForward()` fire nothing. Wrap them:

```ts
engine.transaction("Recolor", () => shape.set("fill", "teal"));
```

## My object reference stopped working after undo

Undo and redo rebuild the changed objects, so they are new instances with the same ids. Keep ids, and look objects up with `engine.getObjectById(id)`.

## My custom properties are lost after loading

With plain `toJSON` and `loadFromJSON`, Fabric.js only saves the properties it knows. Register the class with its custom properties:

```ts
engine.registerObject({ fabricClass: Sticker, properties: ["label"] });
```

## Loading fails with UNKNOWN_OBJECT_TYPE

The document contains a class your editor has not registered. Pass it in `customObjects`, or call `registerObject` before loading. The class needs a static `type` that matches the saved `type`. In plain Fabric.js the same problem shows up as `klass.fromObject is not a function`.

## Two copies of Fabric.js in the bundle

A class registered in one copy is unknown to the other, and loads fail with `UNKNOWN_OBJECT_TYPE`. Make sure only one `fabric` is installed. With a linked package in Vite, add `resolve: { dedupe: ["fabric"] }`.

## Loading fails with MISSING_ASSETS

`error.missingAssets` lists every image that did not load. Fix the URLs with `assets.resolveUrl`, offer a replacement with `assets.replaceMissingImage`, or call `engine.replaceImage(oldUrl, newUrl)` later.

## Text looks different after reopening

The font was not loaded when the text was created, so Fabric.js measured it with a fallback. Load web fonts in `assets.loadFont`, and watch for `FONT_UNAVAILABLE` warnings.

## Export fails with a tainted canvas

An image from another site was loaded without CORS, so the browser blocks PNG, JPEG and WebP export. Load it with `crossOrigin: "anonymous"` from a server that sends `Access-Control-Allow-Origin`. Run `engine.preflightExport({ format })` to show the problem before the user clicks export.

## Saves fail with SAVE_CONFLICT in one tab

Your adapter does not return the new revision. Return `{ revision }` from `saveDocument`, or store `document.revision`.

## The recovery prompt never appears

Pass `recovery: { store: createIndexedDbRecovery() }`. Copies are only written while there are unsaved changes, and private browsing may clear storage when the window closes.

## React: two engines in development

StrictMode mounts effects twice. `useDocumentEngine` destroys the first engine, so only one stays alive. Always use the engine the hook returns.

## Vue: the canvas does not render after loading

Vue wrapped the Fabric.js canvas in a reactive proxy. Keep it in `shallowRef`, or pass `toRaw(canvas)`. See [Vue](https://fabricjs-document-engine.jscrate.dev/docs/frameworks/vue).

## Next.js: "window is not defined"

Fabric.js ran on the server. Load the editor with `dynamic(() => import("./editor"), { ssr: false })`. See [Next.js](https://fabricjs-document-engine.jscrate.dev/docs/frameworks/next-js).

If this Fabric.js troubleshooting list does not cover your problem, [open an issue](https://github.com/re-sohail/fabricjs-document-engine/issues) with the error `code`, the Fabric.js version and the browser.
