Summary
TableHandlesView.mouseMoveHandler decides a block is a table from the DOM alone, then checks only that the schema contains a table block — never that the hovered block is one. Any custom block that renders a real <table> therefore ends up on the table path, and reading its content throws.
Still present in 0.53.0 (found on 0.51.4, verified the same code in the latest).
Where
packages/core/src/extensions/TableHandles/TableHandles.ts
// L212 — the block is decided by walking up the DOM to a TD/TH
const target = domCellAround(event.target);
…
// L262
if (!pmNodeInfo) throw new Error(`Block with ID ${blockEl.id} not found`);
…
// L274 — asks whether the *schema* has a table, not whether *this block* is one
if (editorHasBlockWithType(this.editor, "table")) {
this.tablePos = …;
tableBlock = block;
}
…
// L339
colIndex === tableBlock.content.rows[0].cells.length - 1,
domCellAround returns {type: "cell"} for any TD/TH. Its .ProseMirror bail-out never runs when the event target already is a cell, so nothing an integrator puts on an ancestor can opt out.
Two ways this fires
1. A custom block with content: "none" that renders a table → block.content is undefined:
Uncaught TypeError: Cannot read properties of undefined (reading 'rows')
Merely moving the mouse across the cells is enough — no click, no edit. In our app (an embedded database view drawn inside a document) this filled the console with one error per mouse move.
2. A table inside a nested editor (our synced-block feature embeds a second BlockNote editor in a custom block). The cell belongs to the inner editor, but the outer editor's handler still matches it because this.pmView.dom.contains(event.target) is true, and the id is unknown to the outer document:
Uncaught Error: Block with ID <id> not found
Minimal reproduction
- Add a custom block spec with
content: "none" whose render function returns a plain <table><tbody><tr><td>x</td></tr></tbody></table>.
- Insert it into a document.
- Move the mouse over the cell.
Suggested fix
After resolving the hovered block, check the block itself rather than the schema — e.g. block.type === "table" (or checkBlockIsDefaultType("table", block, this.editor)) — and return early otherwise. The same early return would cover the nested-editor case if the id lookup fails instead of throwing.
Workaround we are using
We wrap mouseMoveHandler and, when the cell is not a real table cell of this editor, call the original with the event's target proxied to the editor root: domCellAround then stops at .ProseMirror and reports "no table", which is the path that hides the handles. Swallowing the event instead is not viable — column resizing listens on window, so stopPropagation breaks it.
Related: #1411 (the colIndex variant, closed without a reproduction).
Summary
TableHandlesView.mouseMoveHandlerdecides a block is a table from the DOM alone, then checks only that the schema contains a table block — never that the hovered block is one. Any custom block that renders a real<table>therefore ends up on the table path, and reading its content throws.Still present in 0.53.0 (found on 0.51.4, verified the same code in the latest).
Where
packages/core/src/extensions/TableHandles/TableHandles.tsdomCellAroundreturns{type: "cell"}for anyTD/TH. Its.ProseMirrorbail-out never runs when the event target already is a cell, so nothing an integrator puts on an ancestor can opt out.Two ways this fires
1. A custom block with
content: "none"that renders a table →block.contentisundefined:Merely moving the mouse across the cells is enough — no click, no edit. In our app (an embedded database view drawn inside a document) this filled the console with one error per mouse move.
2. A table inside a nested editor (our synced-block feature embeds a second BlockNote editor in a custom block). The cell belongs to the inner editor, but the outer editor's handler still matches it because
this.pmView.dom.contains(event.target)is true, and the id is unknown to the outer document:Minimal reproduction
content: "none"whose render function returns a plain<table><tbody><tr><td>x</td></tr></tbody></table>.Suggested fix
After resolving the hovered block, check the block itself rather than the schema — e.g.
block.type === "table"(orcheckBlockIsDefaultType("table", block, this.editor)) — and return early otherwise. The same early return would cover the nested-editor case if the id lookup fails instead of throwing.Workaround we are using
We wrap
mouseMoveHandlerand, when the cell is not a real table cell of this editor, call the original with the event'stargetproxied to the editor root:domCellAroundthen stops at.ProseMirrorand reports "no table", which is the path that hides the handles. Swallowing the event instead is not viable — column resizing listens onwindow, sostopPropagationbreaks it.Related: #1411 (the
colIndexvariant, closed without a reproduction).