Skip to content

fix(xl-docx-exporter): clamp list nesting to the levels DOCX defines - #2969

Merged
nperez0111 merged 1 commit into
TypeCellOS:mainfrom
adarshsm:fix/docx-list-nesting-depth
Aug 13, 2026
Merged

fix(xl-docx-exporter): clamp list nesting to the levels DOCX defines#2969
nperez0111 merged 1 commit into
TypeCellOS:mainfrom
adarshsm:fix/docx-list-nesting-depth

Conversation

@adarshsm

@adarshsm adarshsm commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Fixes #2228.

The bug

numberedListItem and bulletListItem pass the block's nestingLevel straight into docx's numbering.level:

numbering: {
  reference: "blocknote-bullet-list",
  level: nestingLevel,
},

But the numbering configs built in createDefaultDocumentOptions define only 9 levels (w:ilvl 0-8):

levels: Array.from({ length: 9 }, (_, i) => ({ ... }))

Nothing reconciles the two, so a deeply nested list asks for a level that doesn't exist.

Two failures, not one

While writing the regression test I found the reported crash is the louder of two problems:

Nesting depth nestingLevel Behaviour on main
≤ 9 0-8 correct
10 9 no crash, but silently wrong
11+ 10+ throws, whole export fails

At 11 levels or more, docx throws Level cannot be greater than 9 and the entire export is lost — matching the report, where the only feedback is a console error.

At exactly 10 levels it doesn't throw, which is why this half is easy to miss. docx permits level 9, so the export "succeeds" and writes <w:ilvl w:val="9"/> — but numbering.xml defines no level 9. I confirmed this on main by exporting a 10-deep list and comparing the two files:

document.xml   uses  w:ilvl [0,1,2,3,4,5,6,7,8,9]
numbering.xml  defines      0-8  (max defined: 8)

So that last item references an undefined numbering level and loses its bullet and indent.

The fix

Clamp the level to the deepest one the numbering config actually defines. Deeper items render at that level rather than crashing or dangling — the same way Word collapses nesting past its own 9-level limit (the limit docx points at in its error message).

The level count now lives in one shared constant used by both the numbering config and the mappings, so they can't drift apart if the config ever gains levels.

An alternative would be synthesising extra indentation for levels past 8 to keep deep nesting visually distinct. I left that out deliberately: it would fight the indent defined on the numbering level itself, and Word doesn't do it either. Happy to add it if you'd prefer that behaviour.

Testing

Added should clamp list nesting deeper than DOCX supports to docxExporter.test.ts. It exports a 12-deep bullet list and asserts on the emitted w:ilvl sequence:

expect(levels).toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8]);

Asserting the sequence rather than just "did not throw" covers both failures at once — the crash and the undefined-level case — and confirms all 12 items still make it into the document.

Verified in both directions: with only the two source files reverted (test kept), the new test fails with the upstream Level cannot be greater than 9, while the other five tests still pass, so nothing is masking the result. With the fix, all 6 pass.

No snapshots changed, which is the intended outcome — lists within 9 levels are byte-for-byte unaffected.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed DOCX export for deeply nested numbered and bulleted lists.
    • List nesting is now capped at the supported ninth level while preserving all list items.
  • Tests

    • Added regression coverage for exporting lists nested beyond the DOCX nesting limit.

`numberedListItem` and `bulletListItem` passed the block's `nestingLevel`
straight into docx's `numbering.level`, but the numbering configs built in
`createDefaultDocumentOptions` only define 9 levels (`w:ilvl` 0-8).

Lists nested deeper than that produced two distinct failures:

- 11 levels or more made docx throw "Level cannot be greater than 9",
  which aborted the entire export with no feedback beyond a console error.
- Exactly 10 levels did not throw, but emitted `<w:ilvl w:val="9"/>` while
  `numbering.xml` defines no level 9, so that item lost its bullet and
  indent.

Clamp the level to the deepest one the numbering config defines, so deeper
items render at that level instead - the same way Word collapses nesting
past its own 9-level limit. The level count now comes from a single shared
constant, so the config and the mappings cannot drift apart.

Fixes TypeCellOS#2228
@vercel

vercel Bot commented Aug 13, 2026

Copy link
Copy Markdown

@adarshsm is attempting to deploy a commit to the TypeCell Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 74c460fe-88ad-4586-9833-f7872f427127

📥 Commits

Reviewing files that changed from the base of the PR and between 115d433 and 36b0f2e.

📒 Files selected for processing (4)
  • packages/xl-docx-exporter/src/docx/defaultSchema/blocks.ts
  • packages/xl-docx-exporter/src/docx/docxExporter.test.ts
  • packages/xl-docx-exporter/src/docx/docxExporter.ts
  • packages/xl-docx-exporter/src/docx/listLevels.ts

📝 Walkthrough

Walkthrough

DOCX export now defines the supported nine-level list range, clamps deeper numbered and bulleted list nesting to level 8, generates list configurations from the shared limit, and tests deep nested bullet-list export.

Changes

DOCX list-level handling

Layer / File(s) Summary
List-level limit and exporter configuration
packages/xl-docx-exporter/src/docx/listLevels.ts, packages/xl-docx-exporter/src/docx/docxExporter.ts
Defines DOCX_LIST_LEVEL_COUNT = 9 and uses it for numbered and bulleted list-level configuration arrays.
List-level clamping and regression coverage
packages/xl-docx-exporter/src/docx/defaultSchema/blocks.ts, packages/xl-docx-exporter/src/docx/docxExporter.test.ts
Clamps numbered and bulleted list nesting levels to the supported range. The regression test verifies that a 12-level nested bullet list retains all items and uses level 8 for deeper items.

Estimated code review effort: 2 (Simple) | ~10 minutes

Mergeability Score: ⚪ Minimal · up to 36b0f

The change clamps deeply nested DOCX lists to supported numbering levels and adds regression coverage; no actionable merge-blocking risk remains beyond normal checks and review.

Suggested reviewers: nperez0111

Poem

I’m a rabbit with lists in a row,
Nine DOCX levels are all we show.
Deeper hops land at level eight,
No crashing burrow at the gate.
Every item stays in sight—
Exported safely, neat and right.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: clamping DOCX list nesting to supported levels.
Description check ✅ Passed The description clearly explains the bug, rationale, implementation, impact, and regression testing, although it omits some template headings.
Linked Issues check ✅ Passed The changes directly address issue #2228 by preventing DOCX export failures and invalid numbering levels for deeply nested lists.
Out of Scope Changes check ✅ Passed All changes are limited to list-level clamping, shared numbering configuration, and regression coverage for the linked issue.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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.

@nperez0111 nperez0111 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for this @adarshsm

@pkg-pr-new

pkg-pr-new Bot commented Aug 13, 2026

Copy link
Copy Markdown

Open in StackBlitz

@blocknote/ariakit

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

@blocknote/code-block

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

@blocknote/core

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

@blocknote/mantine

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

@blocknote/react

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

@blocknote/server-util

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

@blocknote/shadcn

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

@blocknote/xl-ai

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

@blocknote/xl-docx-exporter

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

@blocknote/xl-email-exporter

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

@blocknote/xl-multi-column

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

@blocknote/xl-odt-exporter

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

@blocknote/xl-pdf-exporter

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

commit: 36b0f2e

@nperez0111
nperez0111 merged commit 78b8bee into TypeCellOS:main Aug 13, 2026
23 of 25 checks passed
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.

DOCX Export: crash when list level above 9

2 participants