BookStackApp/BookStack · error · Error
Unexpected delta format
Error message
Unexpected delta format
What it means
CollabElementNode.applyChildrenYjsDelta applies a Yjs delta to the element's children. Each delta event must contain either a delete or an insert key; when a delta entry has neither (a delta object in an unrecognized shape), the code throws 'Unexpected delta format'. This indicates the Yjs XmlText delta does not match the formats the Lexical yjs binding understands (string insert, shared-type insert, or delete with retain).
Source
Thrown at resources/js/wysiwyg/lexical/yjs/CollabElementNode.ts:224
currIndex += insertDelta.length;
} else {
const sharedType = insertDelta;
const {nodeIndex} = getPositionFromElementAndOffset(
this,
currIndex,
false,
);
const collabNode = $getOrInitCollabNodeFromSharedType(
binding,
sharedType as XmlText | YMap<unknown> | XmlElement,
this,
);
children.splice(nodeIndex, 0, collabNode);
currIndex += 1;
}
} else {
throw new Error('Unexpected delta format');
}
}
}
syncChildrenFromYjs(binding: Binding): void {
// Now diff the children of the collab node with that of our existing Lexical node.
const lexicalNode = this.getNode();
invariant(
lexicalNode !== null,
'syncChildrenFromYjs: could not find element node',
);
const key = lexicalNode.__key;
const prevLexicalChildrenKeys = $createChildrenArray(lexicalNode, null);
const nextLexicalChildrenKeys: Array<NodeKey> = [];
const lexicalChildrenKeysLength = prevLexicalChildrenKeys.length;
const collabChildren = this._children;
const collabChildrenLength = collabChildren.length;View on GitHub (pinned to 18f8469a1c)
Solutions
- Check that all clients use compatible versions of lexical, @lexical/yjs, and yjs; align dependencies across the app.
- Inspect the raw delta (console.log the event in applyChildrenYjsDelta) to identify the unexpected entry and whether a custom transform or plugin produced it.
- Remove or normalize custom Yjs annotations/format deltas on the shared XmlText used by Lexical.
- Reset collab state: re-initialize the document from a fresh Y.Doc / snapshot rather than replaying the problematic update.
- Catch the error around yjsSyncPlugin/update application and force a full re-sync (syncChildrenFromYjs) instead of crashing the editor.
Example fix
// before
provider.on('sync', () => applyUpdates(rawUpdates));
// after
try {
applyUpdates(rawUpdates);
} catch (e) {
if (e.message === 'Unexpected delta format') {
// resync from authoritative doc instead of replaying bad update
editor.update(() => collabCompositor.syncChildrenFromYjs(binding));
} else {
throw e;
}
} Defensive patterns
Strategy: try-catch
Validate before calling
function isKnownDelta(entry) {
return entry != null && (entry.insert !== undefined || entry.delete !== undefined);
}
// validate before applying: delta.every(isKnownDelta) Type guard
function isSupportedDelta(d) {
return d != null && typeof d === 'object' &&
(('insert' in d) || ('delete' in d));
} Try / catch
try {
collabNode.applyChildrenYjsDelta(binding, delta);
} catch (e) {
if (e.message === 'Unexpected delta format') {
console.error('Unrecognized Yjs delta, resyncing:', delta);
resyncFromAuthoritativeDoc();
} else throw e;
} Prevention
- Keep yjs, y-protocols, and @lexical/yjs versions identical across all clients and the server.
- Do not attach custom formats/annotations to the shared XmlText used by Lexical collab.
- Validate or sanitize snapshots/updates before applying them to the collaborative doc.
- Provide a resync path (rebuild from an authoritative Y.Doc) for corrupted updates.
When it happens
Trigger: A Yjs update applied to a CollabElementNode's XmlText whose delta entry contains only retain (no insert/delete), or contains unsupported keys such as a format-only or custom-annotated delta, so both deleteDelta and insertDelta come back null/undefined.
Common situations: Yjs provider/client version mismatch producing deltas the binding wasn't written for (e.g. y-protocols/Yjs upgrade changing delta shapes); custom Yjs transforms or snapshots introducing retain-only or format deltas; corrupted or manually crafted Yjs updates fed to the CollabCompositor; mixing Lexical collab state between incompatible versions.
Related errors
- ${error.toString()} Parent: ${dom.tagName}, new child: {tag:
- Cannot call set() on a frozen Lexical node map
- Cannot call clear() on a frozen Lexical node map
- Cannot call delete() on a frozen Lexical node map
- ${method} is not supported in headless mode
AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02).
Data as JSON: /api/errors/3156c54d39df4852.
Report an issue: GitHub.