Skip to content

fix: restore depth guard in getParentBlockInfo to prevent RangeError#2585

Open
hedi-ghodhbane wants to merge 3 commits intoTypeCellOS:mainfrom
hedi-ghodhbane:fix/getParentBlockInfo-depth-guard
Open

fix: restore depth guard in getParentBlockInfo to prevent RangeError#2585
hedi-ghodhbane wants to merge 3 commits intoTypeCellOS:mainfrom
hedi-ghodhbane:fix/getParentBlockInfo-depth-guard

Conversation

@hedi-ghodhbane
Copy link

@hedi-ghodhbane hedi-ghodhbane commented Mar 23, 2026

Summary

PR #2126 introduced a regression in getParentBlockInfo (mergeBlocks.ts). The depth guard that prevented calling $pos.before(0) on the top-level document node was removed.

When pressing Delete at the end of the last block, $pos.depth is 1, so depth becomes 0, and $pos.before(0) throws:

RangeError: There is no position before the top-level node

This PR restores the guard by returning undefined when depth < 1, before the .before() call.

Test plan

  • Press Delete at the end of the last block in the editor → should be a no-op, no error
  • Press Delete in an empty editor → should be a no-op, no error
  • Forward-delete between blocks should still work as expected (merging blocks)

Fixes #2584

Summary by CodeRabbit

  • Bug Fixes

    • Prevented an edge-case failure when merging blocks at the document root by ensuring no invalid parent resolution occurs for top-level positions, improving stability during block merges.
  • Tests

    • Added a unit test covering the top-level block case to prevent regressions and validate correct behavior when no parent block exists.

PR TypeCellOS#2126 removed the depth guard that prevented calling `$pos.before(0)`
on the top-level document node. When pressing Delete at the end of the
last block, `$pos.depth` is 1, so `depth` becomes 0, and
`$pos.before(0)` throws "RangeError: There is no position before the
top-level node".

This restores the guard by returning undefined when depth < 1, before
the `.before()` call.

Fixes TypeCellOS#2584
@vercel
Copy link

vercel bot commented Mar 23, 2026

@hedi-ghodhbane is attempting to deploy a commit to the TypeCell Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link

coderabbitai bot commented Mar 23, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 25d5a1b2-8d30-40ab-8776-bb3b5a50a9a3

📥 Commits

Reviewing files that changed from the base of the PR and between 41bd668 and 55be6bd.

📒 Files selected for processing (1)
  • packages/core/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/core/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.test.ts

📝 Walkthrough

Walkthrough

A depth guard was added to getParentBlockInfo so it returns undefined when the computed depth is less than 1, preventing calls to $pos.before(0) that previously threw a RangeError for top-level positions.

Changes

Cohort / File(s) Summary
Merge logic
packages/core/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.ts
Added a guard: return undefined when computed depth < 1 to avoid calling $pos.before(0) and throwing a RangeError at top-level positions.
Tests
packages/core/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.test.ts
Imported getParentBlockInfo and added a test asserting it returns undefined for a top-level block position (verifies $pos.depth - 1 < 1 before assertion).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰
I nibbled at depth where the root once bled,
Placed a gentle guard where a RangeError fled.
Now Delete hops lightly, no crash in sight,
Merges stay calm through day and night. 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: restoring a depth guard in getParentBlockInfo to prevent RangeError, which directly addresses the code modifications in the PR.
Description check ✅ Passed The description provides a clear summary of the regression, explains the root cause, and outlines the fix with test plan, but lacks some template sections like Impact and Checklist.
Linked Issues check ✅ Passed The PR fully addresses issue #2584 by restoring the depth guard in getParentBlockInfo and adding a test case verifying the fix prevents the RangeError.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the regression in getParentBlockInfo; the test file update is a necessary part of addressing the linked issue.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@YousefED YousefED requested a review from matthewlipski March 23, 2026 10:02
@nperez0111
Copy link
Contributor

@hedi-ghodhbane thanks for contributing this, ideally we'd have a test case which proves this behavior

@pkg-pr-new
Copy link

pkg-pr-new bot commented Mar 23, 2026

Open in StackBlitz

@blocknote/ariakit

npm i https://pkg.pr.new/@blocknote/ariakit@2585

@blocknote/code-block

npm i https://pkg.pr.new/@blocknote/code-block@2585

@blocknote/core

npm i https://pkg.pr.new/@blocknote/core@2585

@blocknote/mantine

npm i https://pkg.pr.new/@blocknote/mantine@2585

@blocknote/react

npm i https://pkg.pr.new/@blocknote/react@2585

@blocknote/server-util

npm i https://pkg.pr.new/@blocknote/server-util@2585

@blocknote/shadcn

npm i https://pkg.pr.new/@blocknote/shadcn@2585

@blocknote/xl-ai

npm i https://pkg.pr.new/@blocknote/xl-ai@2585

@blocknote/xl-docx-exporter

npm i https://pkg.pr.new/@blocknote/xl-docx-exporter@2585

@blocknote/xl-email-exporter

npm i https://pkg.pr.new/@blocknote/xl-email-exporter@2585

@blocknote/xl-multi-column

npm i https://pkg.pr.new/@blocknote/xl-multi-column@2585

@blocknote/xl-odt-exporter

npm i https://pkg.pr.new/@blocknote/xl-odt-exporter@2585

@blocknote/xl-pdf-exporter

npm i https://pkg.pr.new/@blocknote/xl-pdf-exporter@2585

commit: 55be6bd

sonarly bot pushed a commit to twentyhq/twenty that referenced this pull request Mar 23, 2026
…0.47.1

https://sonarly.com/issue/17525?type=bug

Pressing the Delete (forward-delete) key at the end of the last block in any BlockNote rich text editor throws an unhandled RangeError, crashing the editor. This is caused by a regression in BlockNote 0.47.1's `getParentBlockInfo` function.

Fix: Pinned `@blocknote/mantine`, `@blocknote/react`, `@blocknote/xl-docx-exporter`, and `@blocknote/xl-pdf-exporter` back to exact version `0.47.0` in `packages/twenty-front/package.json`.

BlockNote 0.47.1 introduced a regression in `@blocknote/core` (upstream PR #2126, "handle more delete key cases") that removed a depth guard in `getParentBlockInfo()`. This causes `$pos.before(0)` to be called when the cursor is at the end of the last top-level block and the user presses Delete, throwing `RangeError: There is no position before the top-level node`.

The dependabot commit `6a2e0182ab` bumped these from exact `"0.47.0"` to `"^0.47.1"`, pulling in the buggy release. This pin restores the exact versions from the original deliberate upgrade (commit `4ed09a3feb`).

The server-side `@blocknote/server-util` is left unchanged at `^0.47.1` since it's a headless package that doesn't use ProseMirror's view layer or keyboard handlers — the regression only affects browser-side editing.

Once BlockNote publishes a fix (tracked in upstream issue [#2584](TypeCellOS/BlockNote#2584), fix PR [#2585](TypeCellOS/BlockNote#2585)), these packages can be upgraded again.
@hedi-ghodhbane
Copy link
Author

@nperez0111 I'm not sure what you mean by a test case. You mean a record / demo of the issue? or a Unit test?

@nperez0111
Copy link
Contributor

@hedi-ghodhbane I mean more of a unit test which proves that this does what it intended to. There is a test file next to the one you modified: https://github.com/hedi-ghodhbane/BlockNote/blob/4bbfd90ee0749fa038a500e043021d19bbb43916/packages/core/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.test.ts

Verifies that getParentBlockInfo returns undefined when called on a
top-level block (depth < 1), preventing the RangeError that was thrown
by $pos.before(0).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@hedi-ghodhbane
Copy link
Author

hedi-ghodhbane commented Mar 23, 2026

@nperez0111 Added a unit test in 41bd6683b that verifies getParentBlockInfo returns undefined for top-level blocks instead of throwing.

Without the depth guard (reproduces the bug):

× |@blocknote/core| > Test mergeBlocks > getParentBlockInfo returns undefined for top-level block
FAIL  RangeError: There is no position before the top-level node
    884|  throw new RangeError("There is no position before the top-…
 ❯ Module.getParentBlockInfo src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.ts:20:32
 Test Files  1 failed (1)
      Tests  1 failed | 11 passed (12)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/core/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.test.ts (1)

80-88: Tighten this regression test by asserting the depth-guard precondition.

Right now the test only checks the return value. Please also assert that the resolved position is in the depth < 1 guard path so the test can’t pass for an unrelated undefined case.

Suggested test hardening
   const beforePos = getPosBeforeSelectedBlock();
   const doc = getEditor()._tiptapEditor.state.doc;
+  const resolved = doc.resolve(beforePos);
+  expect(resolved.depth - 1).toBeLessThan(1);
   const result = getParentBlockInfo(doc, beforePos);

   expect(result).toBeUndefined();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/core/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.test.ts`
around lines 80 - 88, The test should also assert the resolved position meets
the depth guard to ensure we exercise the "top-level" branch: after computing
beforePos via getPosBeforeSelectedBlock() and doc from
getEditor()._tiptapEditor.state.doc, resolve the position (use
doc.resolve(beforePos) or the same helper used in getParentBlockInfo) and assert
resolvedPos.depth < 1 before calling getParentBlockInfo; keep the existing
expect(result).toBeUndefined() to validate the return value for the top-level
case (functions referenced: getParentBlockInfo, getPosBeforeSelectedBlock,
getEditor).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@packages/core/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.test.ts`:
- Around line 80-88: The test should also assert the resolved position meets the
depth guard to ensure we exercise the "top-level" branch: after computing
beforePos via getPosBeforeSelectedBlock() and doc from
getEditor()._tiptapEditor.state.doc, resolve the position (use
doc.resolve(beforePos) or the same helper used in getParentBlockInfo) and assert
resolvedPos.depth < 1 before calling getParentBlockInfo; keep the existing
expect(result).toBeUndefined() to validate the return value for the top-level
case (functions referenced: getParentBlockInfo, getPosBeforeSelectedBlock,
getEditor).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 82547264-af75-450d-a460-65416afc1ed9

📥 Commits

Reviewing files that changed from the base of the PR and between 4bbfd90 and 41bd668.

📒 Files selected for processing (1)
  • packages/core/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.test.ts

Verify that the resolved position's depth is < 1 before calling
getParentBlockInfo, ensuring we exercise the top-level branch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@hedi-ghodhbane
Copy link
Author

Coderabbit comment fixed :)

Copy link
Contributor

@nperez0111 nperez0111 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @hedi-ghodhbane!

@vercel
Copy link

vercel bot commented Mar 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
blocknote Error Error Mar 24, 2026 9:01am
blocknote-website Ready Ready Preview Mar 24, 2026 9:01am

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RangeError in getParentBlockInfo when pressing Delete at end of last block (regression from #2126)

2 participants