Repository: TypeCellOS/BlockNote (2026-04-07T08:35:41Z)

Issue:
RangeError: Position out of range when deleting after multi-column block
### What’s broken?

When using the multi-column feature, continuously pressing Backspace after typing in a block following a multi-column layout causes a runtime error:

```RangeError: Position X out of range```

This crashes the editor and breaks further interaction.

<img width="1225" height="838" alt="Image" src="https://github.com/user-attachments/assets/de3d5796-0967-40e8-bbd3-98c4147efaec" />

<img width="592" height="174" alt="Image" src="https://github.com/user-attachments/assets/3ccd0b6d-4eed-46c0-8193-34692e7e225f" />

### What did you expect to happen?

- Text should be deleted normally
- Editor should remain stable
- No runtime errors should occur


### Steps to reproduce

1. Insert a multi-column block (e.g., 3 columns)
2. Move cursor to the next block (below the columns)
3. Type some text (e.g., aaa)
4. Press and hold Backspace to delete continuously

### BlockNote version

"@blocknote/xl-multi-column": "0.47.1"

### Environment

Chrome

### Additional context

- This issue appears to be related to how the multi-column structure interacts with deletion logic.
- Likely caused by an invalid ProseMirror transaction when deleting across structural boundaries.
- Happens more frequently when holding down Backspace (rapid consecutive deletes).

### Contribution

- [ ] I'd be interested in contributing a fix for this issue

### Sponsor

- [ ] I'm a [sponsor](https://www.blocknotejs.org/pricing) and would appreciate if you could look into this sooner than later 💖


Discussion:
I couldn't replicate this bug exactly, but did find a related bug that might be causing this, addressed in #2629 



Patch:
diff --git a/packages/core/src/extensions/tiptap-extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts b/packages/core/src/extensions/tiptap-extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts
index c0578d7f38..7fc2a413c8 100644
--- a/packages/core/src/extensions/tiptap-extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts
+++ b/packages/core/src/extensions/tiptap-extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts
@@ -128,6 +128,13 @@ export const KeyboardShortcutsExtension = Extension.create<{
               return false;
             }
 
+            const selectionAtBlockStart =
+              state.selection.from ===
+              blockInfo.blockContent.beforePos + 1;
+            if (!selectionAtBlockStart) {
+              return false;
+            }
+
             const prevBlockInfo = getPrevBlockInfo(
               state.doc,
               blockInfo.bnBlock.beforePos,
@@ -454,8 +461,8 @@ export const KeyboardShortcutsExtension = Extension.create<{
 
             return false;
           }),
-        // If the previous block is a columnList, moves the current block to
-        // the end of the last column in it.
+        // If the next block is a columnList, moves the first block from its
+        // first column to after the current block.
         () =>
           commands.command(({ state, tr, dispatch }) => {
             const blockInfo = getBlockInfoFromSelection(state);
@@ -463,6 +470,13 @@ export const KeyboardShortcutsExtension = Extension.create<{
               return false;
             }
 
+            const selectionAtBlockEnd =
+              state.selection.from ===
+              blockInfo.blockContent.afterPos - 1;
+            if (!selectionAtBlockEnd) {
+              return false;
+            }
+
             const nextBlockInfo = getNextBlockInfo(
               state.doc,
               blockInfo.bnBlock.beforePos,


Test patch:
diff --git a/packages/xl-multi-column/src/test/commands/__snapshots__/backspace.test.ts.snap b/packages/xl-multi-column/src/test/commands/__snapshots__/backspace.test.ts.snap
new file mode 100644
index 0000000000..1dadb64533
--- /dev/null
+++ b/packages/xl-multi-column/src/test/commands/__snapshots__/backspace.test.ts.snap
@@ -0,0 +1,696 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`Backspace with multi-column > backspace at block start should move block into last column 1`] = `
+[
+  {
+    "children": [
+      {
+        "children": [
+          {
+            "children": [],
+            "content": [
+              {
+                "styles": {},
+                "text": "col1",
+                "type": "text",
+              },
+            ],
+            "id": "col1-para",
+            "props": {
+              "backgroundColor": "default",
+              "textAlignment": "left",
+              "textColor": "default",
+            },
+            "type": "paragraph",
+          },
+        ],
+        "content": undefined,
+        "id": "1",
+        "props": {
+          "width": 1,
+        },
+        "type": "column",
+      },
+      {
+        "children": [
+          {
+            "children": [],
+            "content": [
+              {
+                "styles": {},
+                "text": "col2",
+                "type": "text",
+              },
+            ],
+            "id": "col2-para",
+            "props": {
+              "backgroundColor": "default",
+              "textAlignment": "left",
+              "textColor": "default",
+            },
+            "type": "paragraph",
+          },
+        ],
+        "content": undefined,
+        "id": "2",
+        "props": {
+          "width": 1,
+        },
+        "type": "column",
+      },
+      {
+        "children": [
+          {
+            "children": [],
+            "content": [
+              {
+                "styles": {},
+                "text": "hello",
+                "type": "text",
+              },
+            ],
+            "id": "col3-para",
+            "props": {
+              "backgroundColor": "default",
+              "textAlignment": "left",
+              "textColor": "default",
+            },
+            "type": "paragraph",
+          },
+          {
+            "children": [],
+            "content": [
+              {
+                "styles": {},
+                "text": " world",
+                "type": "text",
+              },
+            ],
+            "id": "below-para",
+            "props": {
+              "backgroundColor": "default",
+              "textAlignment": "left",
+              "textColor": "default",
+            },
+            "type": "paragraph",
+          },
+        ],
+        "content": undefined,
+        "id": "3",
+        "props": {
+          "width": 1,
+        },
+        "type": "column",
+      },
+    ],
+    "content": undefined,
+    "id": "0",
+    "props": {},
+    "type": "columnList",
+  },
+  {
+    "children": [],
+    "content": [],
+    "id": "4",
+    "props": {
+      "backgroundColor": "default",
+      "textAlignment": "left",
+      "textColor": "default",
+    },
+    "type": "paragraph",
+  },
+]
+`;
+
+exports[`Backspace with multi-column > mid-text backspace next to columnList should not move block 1`] = `
+[
+  {
+    "children": [
+      {
+        "children": [
+          {
+            "children": [],
+            "content": [
+              {
+                "styles": {},
+                "text": "col1",
+                "type": "text",
+              },
+            ],
+            "id": "col1-para",
+            "props": {
+              "backgroundColor": "default",
+              "textAlignment": "left",
+              "textColor": "default",
+            },
+            "type": "paragraph",
+          },
+        ],
+        "content": undefined,
+        "id": "1",
+        "props": {
+          "width": 1,
+        },
+        "type": "column",
+      },
+      {
+        "children": [
+          {
+            "children": [],
+            "content": [
+              {
+                "styles": {},
+                "text": "col2",
+                "type": "text",
+              },
+            ],
+            "id": "col2-para",
+            "props": {
+              "backgroundColor": "default",
+              "textAlignment": "left",
+              "textColor": "default",
+            },
+            "type": "paragraph",
+          },
+        ],
+        "content": undefined,
+        "id": "2",
+        "props": {
+          "width": 1,
+        },
+        "type": "column",
+      },
+      {
+        "children": [
+          {
+            "children": [],
+            "content": [
+              {
+                "styles": {},
+                "text": "hello",
+                "type": "text",
+              },
+            ],
+            "id": "col3-para",
+            "props": {
+              "backgroundColor": "default",
+              "textAlignment": "left",
+              "textColor": "default",
+            },
+            "type": "paragraph",
+          },
+        ],
+        "content": undefined,
+        "id": "3",
+        "props": {
+          "width": 1,
+        },
+        "type": "column",
+      },
+    ],
+    "content": undefined,
+    "id": "0",
+    "props": {},
+    "type": "columnList",
+  },
+  {
+    "children": [],
+    "content": [
+      {
+        "styles": {},
+        "text": "hello world",
+        "type": "text",
+      },
+    ],
+    "id": "below-para",
+    "props": {
+      "backgroundColor": "default",
+      "textAlignment": "left",
+      "textColor": "default",
+    },
+    "type": "paragraph",
+  },
+  {
+    "children": [],
+    "content": [],
+    "id": "4",
+    "props": {
+      "backgroundColor": "default",
+      "textAlignment": "left",
+      "textColor": "default",
+    },
+    "type": "paragraph",
+  },
+]
+`;
+
+exports[`Backspace with multi-column > second backspace should merge into previous block in column 1`] = `
+[
+  {
+    "children": [
+      {
+        "children": [
+          {
+            "children": [],
+            "content": [
+              {
+                "styles": {},
+                "text": "col1",
+                "type": "text",
+              },
+            ],
+            "id": "col1-para",
+            "props": {
+              "backgroundColor": "default",
+              "textAlignment": "left",
+              "textColor": "default",
+            },
+            "type": "paragraph",
+          },
+        ],
+        "content": undefined,
+        "id": "1",
+        "props": {
+          "width": 1,
+        },
+        "type": "column",
+      },
+      {
+        "children": [
+          {
+            "children": [],
+            "content": [
+              {
+                "styles": {},
+                "text": "col2",
+                "type": "text",
+              },
+            ],
+            "id": "col2-para",
+            "props": {
+              "backgroundColor": "default",
+              "textAlignment": "left",
+              "textColor": "default",
+            },
+            "type": "paragraph",
+          },
+        ],
+        "content": undefined,
+        "id": "2",
+        "props": {
+          "width": 1,
+        },
+        "type": "column",
+      },
+      {
+        "children": [
+          {
+            "children": [],
+            "content": [
+              {
+                "styles": {},
+                "text": "hello world",
+                "type": "text",
+              },
+            ],
+            "id": "col3-para",
+            "props": {
+              "backgroundColor": "default",
+              "textAlignment": "left",
+              "textColor": "default",
+            },
+            "type": "paragraph",
+          },
+        ],
+        "content": undefined,
+        "id": "3",
+        "props": {
+          "width": 1,
+        },
+        "type": "column",
+      },
+    ],
+    "content": undefined,
+    "id": "0",
+    "props": {},
+    "type": "columnList",
+  },
+  {
+    "children": [],
+    "content": [],
+    "id": "4",
+    "props": {
+      "backgroundColor": "default",
+      "textAlignment": "left",
+      "textColor": "default",
+    },
+    "type": "paragraph",
+  },
+]
+`;
+
+exports[`Delete with multi-column > delete at block end should move first column block out 1`] = `
+[
+  {
+    "children": [],
+    "content": [
+      {
+        "styles": {},
+        "text": "hello ",
+        "type": "text",
+      },
+    ],
+    "id": "above-para",
+    "props": {
+      "backgroundColor": "default",
+      "textAlignment": "left",
+      "textColor": "default",
+    },
+    "type": "paragraph",
+  },
+  {
+    "children": [],
+    "content": [
+      {
+        "styles": {},
+        "text": "world",
+        "type": "text",
+      },
+    ],
+    "id": "col1-para",
+    "props": {
+      "backgroundColor": "default",
+      "textAlignment": "left",
+      "textColor": "default",
+    },
+    "type": "paragraph",
+  },
+  {
+    "children": [
+      {
+        "children": [
+          {
+            "children": [],
+            "content": [
+              {
+                "styles": {},
+                "text": "universe",
+                "type": "text",
+              },
+            ],
+            "id": "col2-para",
+            "props": {
+              "backgroundColor": "default",
+              "textAlignment": "left",
+              "textColor": "default",
+            },
+            "type": "paragraph",
+          },
+        ],
+        "content": undefined,
+        "id": "2",
+        "props": {
+          "width": 1,
+        },
+        "type": "column",
+      },
+      {
+        "children": [
+          {
+            "children": [],
+            "content": [
+              {
+                "styles": {},
+                "text": "planet",
+                "type": "text",
+              },
+            ],
+            "id": "col3-para",
+            "props": {
+              "backgroundColor": "default",
+              "textAlignment": "left",
+              "textColor": "default",
+            },
+            "type": "paragraph",
+          },
+        ],
+        "content": undefined,
+        "id": "3",
+        "props": {
+          "width": 1,
+        },
+        "type": "column",
+      },
+    ],
+    "content": undefined,
+    "id": "0",
+    "props": {},
+    "type": "columnList",
+  },
+  {
+    "children": [],
+    "content": [],
+    "id": "4",
+    "props": {
+      "backgroundColor": "default",
+      "textAlignment": "left",
+      "textColor": "default",
+    },
+    "type": "paragraph",
+  },
+]
+`;
+
+exports[`Delete with multi-column > mid-text delete next to columnList should not move block 1`] = `
+[
+  {
+    "children": [],
+    "content": [
+      {
+        "styles": {},
+        "text": "hello world",
+        "type": "text",
+      },
+    ],
+    "id": "above-para",
+    "props": {
+      "backgroundColor": "default",
+      "textAlignment": "left",
+      "textColor": "default",
+    },
+    "type": "paragraph",
+  },
+  {
+    "children": [
+      {
+        "children": [
+          {
+            "children": [],
+            "content": [
+              {
+                "styles": {},
+                "text": "world",
+                "type": "text",
+              },
+            ],
+            "id": "col1-para",
+            "props": {
+              "backgroundColor": "default",
+              "textAlignment": "left",
+              "textColor": "default",
+            },
+            "type": "paragraph",
+          },
+        ],
+        "content": undefined,
+        "id": "1",
+        "props": {
+          "width": 1,
+        },
+        "type": "column",
+      },
+      {
+        "children": [
+          {
+            "children": [],
+            "content": [
+              {
+                "styles": {},
+                "text": "universe",
+                "type": "text",
+              },
+            ],
+            "id": "col2-para",
+            "props": {
+              "backgroundColor": "default",
+              "textAlignment": "left",
+              "textColor": "default",
+            },
+            "type": "paragraph",
+          },
+        ],
+        "content": undefined,
+        "id": "2",
+        "props": {
+          "width": 1,
+        },
+        "type": "column",
+      },
+      {
+        "children": [
+          {
+            "children": [],
+            "content": [
+              {
+                "styles": {},
+                "text": "planet",
+                "type": "text",
+              },
+            ],
+            "id": "col3-para",
+            "props": {
+              "backgroundColor": "default",
+              "textAlignment": "left",
+              "textColor": "default",
+            },
+            "type": "paragraph",
+          },
+        ],
+        "content": undefined,
+        "id": "3",
+        "props": {
+          "width": 1,
+        },
+        "type": "column",
+      },
+    ],
+    "content": undefined,
+    "id": "0",
+    "props": {},
+    "type": "columnList",
+  },
+  {
+    "children": [],
+    "content": [],
+    "id": "4",
+    "props": {
+      "backgroundColor": "default",
+      "textAlignment": "left",
+      "textColor": "default",
+    },
+    "type": "paragraph",
+  },
+]
+`;
+
+exports[`Delete with multi-column > second delete should merge first column block into paragraph 1`] = `
+[
+  {
+    "children": [],
+    "content": [
+      {
+        "styles": {},
+        "text": "hello ",
+        "type": "text",
+      },
+    ],
+    "id": "above-para",
+    "props": {
+      "backgroundColor": "default",
+      "textAlignment": "left",
+      "textColor": "default",
+    },
+    "type": "paragraph",
+  },
+  {
+    "children": [],
+    "content": [
+      {
+        "styles": {},
+        "text": "world",
+        "type": "text",
+      },
+    ],
+    "id": "col1-para",
+    "props": {
+      "backgroundColor": "default",
+      "textAlignment": "left",
+      "textColor": "default",
+    },
+    "type": "paragraph",
+  },
+  {
+    "children": [
+      {
+        "children": [
+          {
+            "children": [],
+            "content": [
+              {
+                "styles": {},
+                "text": "universe",
+                "type": "text",
+              },
+            ],
+            "id": "col2-para",
+            "props": {
+              "backgroundColor": "default",
+              "textAlignment": "left",
+              "textColor": "default",
+            },
+            "type": "paragraph",
+          },
+        ],
+        "content": undefined,
+        "id": "2",
+        "props": {
+          "width": 1,
+        },
+        "type": "column",
+      },
+      {
+        "children": [
+          {
+            "children": [],
+            "content": [
+              {
+                "styles": {},
+                "text": "planet",
+                "type": "text",
+              },
+            ],
+            "id": "col3-para",
+            "props": {
+              "backgroundColor": "default",
+              "textAlignment": "left",
+              "textColor": "default",
+            },
+            "type": "paragraph",
+          },
+        ],
+        "content": undefined,
+        "id": "3",
+        "props": {
+          "width": 1,
+        },
+        "type": "column",
+      },
+    ],
+    "content": undefined,
+    "id": "0",
+    "props": {},
+    "type": "columnList",
+  },
+  {
+    "children": [],
+    "content": [],
+    "id": "4",
+    "props": {
+      "backgroundColor": "default",
+      "textAlignment": "left",
+      "textColor": "default",
+    },
+    "type": "paragraph",
+  },
+]
+`;
diff --git a/packages/xl-multi-column/src/test/commands/backspace.test.ts b/packages/xl-multi-column/src/test/commands/backspace.test.ts
new file mode 100644
index 0000000000..d5ebea4c2c
--- /dev/null
+++ b/packages/xl-multi-column/src/test/commands/backspace.test.ts
@@ -0,0 +1,202 @@
+import { describe, expect, it } from "vitest";
+
+import { setupTestEnv } from "../setupTestEnv.js";
+import { BlockNoteEditor } from "@blocknote/core";
+
+const getEditor = setupTestEnv();
+
+function pressBackspace(editor: BlockNoteEditor<any, any, any>) {
+  const view = editor._tiptapEditor.view;
+  const event = new KeyboardEvent("keydown", {
+    key: "Backspace",
+    code: "Backspace",
+    keyCode: 8,
+    bubbles: true,
+  });
+  view.someProp("handleKeyDown", (f: any) => f(view, event));
+}
+
+function pressDelete(editor: BlockNoteEditor<any, any, any>) {
+  const view = editor._tiptapEditor.view;
+  const event = new KeyboardEvent("keydown", {
+    key: "Delete",
+    code: "Delete",
+    keyCode: 46,
+    bubbles: true,
+  });
+  view.someProp("handleKeyDown", (f: any) => f(view, event));
+}
+
+const threeColumnsWithParagraphBelow = [
+  {
+    type: "columnList" as const,
+    children: [
+      {
+        type: "column" as const,
+        children: [
+          { id: "col1-para", type: "paragraph" as const, content: "col1" },
+        ],
+      },
+      {
+        type: "column" as const,
+        children: [
+          { id: "col2-para", type: "paragraph" as const, content: "col2" },
+        ],
+      },
+      {
+        type: "column" as const,
+        children: [
+          { id: "col3-para", type: "paragraph" as const, content: "hello" },
+        ],
+      },
+    ],
+  },
+];
+
+const threeColumnsWithParagraphAbove = [
+  {
+    type: "columnList" as const,
+    children: [
+      {
+        type: "column" as const,
+        children: [
+          { id: "col1-para", type: "paragraph" as const, content: "world" },
+        ],
+      },
+      {
+        type: "column" as const,
+        children: [
+          {
+            id: "col2-para",
+            type: "paragraph" as const,
+            content: "universe",
+          },
+        ],
+      },
+      {
+        type: "column" as const,
+        children: [
+          { id: "col3-para", type: "paragraph" as const, content: "planet" },
+        ],
+      },
+    ],
+  },
+];
+
+describe("Backspace with multi-column", () => {
+  // TODO: When migrating to vitest browser mode, replace this test with
+  // a version that presses Backspace 5 times from offset 5 in "hello world"
+  // and asserts " world" remains. Character deletion relies on contentEditable
+  // which is not available in jsdom.
+  it("mid-text backspace next to columnList should not move block", () => {
+    const editor = getEditor();
+    editor.replaceBlocks(editor.document, [
+      ...threeColumnsWithParagraphBelow,
+      {
+        id: "below-para",
+        type: "paragraph" as const,
+        content: "hello world",
+      },
+    ]);
+
+    // Place cursor at offset 5 (after "hello", mid-text).
+    editor.setTextCursorPosition("below-para", "start");
+    const view = editor._tiptapEditor.view;
+    const startPos = editor.transact((tr) => tr.selection.$from.pos);
+    view.dispatch(
+      view.state.tr.setSelection(
+        (view.state.selection.constructor as any).create(
+          view.state.doc,
+          startPos + 5,
+        ),
+      ),
+    );
+
+    pressBackspace(editor);
+    expect(editor.document).toMatchSnapshot();
+  });
+
+  it("backspace at block start should move block into last column", () => {
+    const editor = getEditor();
+    editor.replaceBlocks(editor.document, [
+      ...threeColumnsWithParagraphBelow,
+      { id: "below-para", type: "paragraph" as const, content: " world" },
+    ]);
+
+    editor.setTextCursorPosition("below-para", "start");
+    pressBackspace(editor);
+    expect(editor.document).toMatchSnapshot();
+  });
+
+  it("second backspace should merge into previous block in column", () => {
+    const editor = getEditor();
+    editor.replaceBlocks(editor.document, [
+      ...threeColumnsWithParagraphBelow,
+      { id: "below-para", type: "paragraph" as const, content: " world" },
+    ]);
+
+    editor.setTextCursorPosition("below-para", "start");
+    pressBackspace(editor);
+    pressBackspace(editor);
+    expect(editor.document).toMatchSnapshot();
+  });
+});
+
+describe("Delete with multi-column", () => {
+  // TODO: When migrating to vitest browser mode, replace this test with
+  // a version that presses Delete 5 times from offset 6 in "hello world"
+  // and asserts "hello " remains. Character deletion relies on contentEditable
+  // which is not available in jsdom.
+  it("mid-text delete next to columnList should not move block", () => {
+    const editor = getEditor();
+    editor.replaceBlocks(editor.document, [
+      {
+        id: "above-para",
+        type: "paragraph" as const,
+        content: "hello world",
+      },
+      ...threeColumnsWithParagraphAbove,
+    ]);
+
+    // Place cursor at offset 6 (after "hello ", mid-text).
+    editor.setTextCursorPosition("above-para", "start");
+    const view = editor._tiptapEditor.view;
+    const startPos = editor.transact((tr) => tr.selection.$from.pos);
+    view.dispatch(
+      view.state.tr.setSelection(
+        (view.state.selection.constructor as any).create(
+          view.state.doc,
+          startPos + 6,
+        ),
+      ),
+    );
+
+    pressDelete(editor);
+    expect(editor.document).toMatchSnapshot();
+  });
+
+  it("delete at block end should move first column block out", () => {
+    const editor = getEditor();
+    editor.replaceBlocks(editor.document, [
+      { id: "above-para", type: "paragraph" as const, content: "hello " },
+      ...threeColumnsWithParagraphAbove,
+    ]);
+
+    editor.setTextCursorPosition("above-para", "end");
+    pressDelete(editor);
+    expect(editor.document).toMatchSnapshot();
+  });
+
+  it("second delete should merge first column block into paragraph", () => {
+    const editor = getEditor();
+    editor.replaceBlocks(editor.document, [
+      { id: "above-para", type: "paragraph" as const, content: "hello " },
+      ...threeColumnsWithParagraphAbove,
+    ]);
+
+    editor.setTextCursorPosition("above-para", "end");
+    pressDelete(editor);
+    pressDelete(editor);
+    expect(editor.document).toMatchSnapshot();
+  });
+});