Skip to content

fix(core): repair invalid content before mounting - #8144

Open
bdbch wants to merge 3 commits into
mainfrom
bdbch/fix-content-validation-throw
Open

fix(core): repair invalid content before mounting#8144
bdbch wants to merge 3 commits into
mainfrom
bdbch/fix-content-validation-throw

Conversation

@bdbch

@bdbch bdbch commented Jul 30, 2026

Copy link
Copy Markdown
Member

Fixes

  • N/A

Changes and Review

This PR fixes a bug where content passed into the editor content could lock up clients when the content is not valid, even when the content was checked via enableContentCheck.

Now the content will be repaired when content is invalid. This is happening by going through node children and wrapping/unwrapping invalid childs + repairing and deduping marks. This happens only on the editor constructor - we may extend this logic to other situations if needed.

Unknown content will still be dropped. Just salvaging unknown content and turning it back into the default node type is itself lossy and could destroy important nodes and attributes.

Checklist

  • I have added a changeset if necessary.
  • I have added tests if possible.
  • I have made sure to test my changes myself.

Responsibility

  • I have reviewed and understand these changes, and I take responsibility for this PR, even if an AI agent created it.

(copy of #8143 which was accidentially closed)

@bdbch
bdbch requested a review from a team as a code owner July 30, 2026 09:51
@netlify

netlify Bot commented Jul 30, 2026

Copy link
Copy Markdown

Deploy Preview for tiptap-embed ready!

Name Link
🔨 Latest commit ee8de71
🔍 Latest deploy log https://app.netlify.com/projects/tiptap-embed/deploys/6a6b1e9d38a1060008a4511f
😎 Deploy Preview https://deploy-preview-8144--tiptap-embed.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@changeset-bot

changeset-bot Bot commented Jul 30, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ee8de71

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 74 packages
Name Type
@tiptap/core Patch
@tiptap/extension-audio Patch
@tiptap/extension-blockquote Patch
@tiptap/extension-bold Patch
@tiptap/extension-bubble-menu Patch
@tiptap/extension-code-block-lowlight Patch
@tiptap/extension-code-block Patch
@tiptap/extension-code Patch
@tiptap/extension-collaboration-caret Patch
@tiptap/extension-collaboration Patch
@tiptap/extension-details Patch
@tiptap/extension-document Patch
@tiptap/extension-drag-handle Patch
@tiptap/extension-emoji Patch
@tiptap/extension-file-handler Patch
@tiptap/extension-find-and-replace Patch
@tiptap/extension-floating-menu Patch
@tiptap/extension-hard-break Patch
@tiptap/extension-heading Patch
@tiptap/extension-highlight Patch
@tiptap/extension-horizontal-rule Patch
@tiptap/extension-image Patch
@tiptap/extension-invisible-characters Patch
@tiptap/extension-italic Patch
@tiptap/extension-link Patch
@tiptap/extension-list Patch
@tiptap/extension-mathematics Patch
@tiptap/extension-mention Patch
@tiptap/extension-node-range Patch
@tiptap/extension-paragraph Patch
@tiptap/extension-ruby-text Patch
@tiptap/extension-strike Patch
@tiptap/extension-subscript Patch
@tiptap/extension-superscript Patch
@tiptap/extension-table-of-contents Patch
@tiptap/extension-table Patch
@tiptap/extension-text-align Patch
@tiptap/extension-text-style Patch
@tiptap/extension-text Patch
@tiptap/extension-twitch Patch
@tiptap/extension-typography Patch
@tiptap/extension-underline Patch
@tiptap/extension-unique-id Patch
@tiptap/extension-youtube Patch
@tiptap/extensions Patch
@tiptap/html Patch
@tiptap/markdown Patch
@tiptap/react Patch
@tiptap/static-renderer Patch
@tiptap/suggestion Patch
@tiptap/vue-2 Patch
@tiptap/vue-3 Patch
@tiptap/extension-drag-handle-react Patch
@tiptap/extension-drag-handle-vue-2 Patch
@tiptap/extension-drag-handle-vue-3 Patch
@tiptap/extension-bullet-list Patch
@tiptap/extension-ordered-list Patch
@tiptap/extension-list-item Patch
@tiptap/extension-list-keymap Patch
@tiptap/extension-task-item Patch
@tiptap/extension-task-list Patch
@tiptap/extension-table-cell Patch
@tiptap/extension-table-header Patch
@tiptap/extension-table-row Patch
@tiptap/extension-color Patch
@tiptap/extension-font-family Patch
@tiptap/extension-character-count Patch
@tiptap/extension-dropcursor Patch
@tiptap/extension-focus Patch
@tiptap/extension-gapcursor Patch
@tiptap/extension-history Patch
@tiptap/extension-placeholder Patch
@tiptap/pm Patch
@tiptap/starter-kit Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary

  • Repairs invalid initial editor content before mounting.
  • Fixes misplaced or nested nodes, missing required content, and invalid or duplicate marks while keeping text when possible.
  • Drops unknown content and falls back to an empty document if repair fails.
  • Avoids logging invalid content values.
  • Adds tests for repair behavior and nested documents.
  • Includes a patch changeset for @tiptap/core.

Walkthrough

Adds schema-aware repair for invalid ProseMirror content, applies it before editor initialization, exports the helper, and adds tests for malformed nodes, marks, nested documents, and content-error handling.

Changes

Invalid content repair

Layer / File(s) Summary
Schema-aware repair engine
packages/core/src/helpers/repairNode.ts, packages/core/__tests__/repairNode.spec.ts
Recursively repairs node structure and marks by fitting, wrapping, unwrapping, moving, or filling content, with tests covering malformed documents and marks.
Editor initialization integration
packages/core/src/Editor.ts, packages/core/src/helpers/index.ts, packages/core/__tests__/onContentError.spec.ts, .changeset/2026-07-30-repair-invalid-initial-content.md
Validates and repairs fallback documents before editor state creation, exports repairNode, tests nested-document recovery, and records a patch changeset.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Editor
  participant ProseMirrorNode
  participant repairNode
  participant EditorState
  Editor->>ProseMirrorNode: Create fallback document
  Editor->>ProseMirrorNode: Check schema validity
  Editor->>repairNode: Repair invalid content
  repairNode-->>Editor: Return schema-valid document
  Editor->>EditorState: Initialize with repaired document
Loading

Possibly related PRs

Suggested labels: area: core, complexity: expert, impact: medium

Suggested reviewers: arnaugomez, alexvcasillas

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: repairing invalid editor content before mounting.
Description check ✅ Passed The description includes the required sections, a clear change summary, and the checklist is filled out.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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.

@coderabbitai coderabbitai Bot added area: core Core editor engine and ProseMirror integration complexity: expert Deep domain knowledge required, high risk impact: medium Affects some users or workflows labels Jul 30, 2026

@coderabbitai coderabbitai Bot 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.

🧹 Nitpick comments (2)
packages/core/__tests__/repairNode.spec.ts (1)

1-207: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

No test coverage for the "repair fails entirely" fallback path. Both files test only the repairable cases; neither exercises repairNode returning null or the resulting Editor empty-document fallback, despite this being an explicit PR objective.

  • packages/core/__tests__/repairNode.spec.ts#L1-L207: add a unit test with a node type whose content can never satisfy fillBefore (e.g. a required child type with no valid default fill), asserting repairNode(...) returns null.
  • packages/core/__tests__/onContentError.spec.ts#L286-L341: add an integration test where content repair cannot succeed, asserting the Editor falls back to an empty document (via this.schema.topNodeType.createAndFill()) instead of crashing.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/__tests__/repairNode.spec.ts` around lines 1 - 207, Add unit
coverage in packages/core/__tests__/repairNode.spec.ts (lines 1-207) using a
node type whose required content cannot be satisfied by fillBefore, and assert
repairNode returns null. Add integration coverage in
packages/core/__tests__/onContentError.spec.ts (lines 286-341) with
unrecoverable content, asserting the Editor falls back to an empty document via
schema.topNodeType.createAndFill() instead of throwing.

Source: Coding guidelines

packages/core/src/Editor.ts (1)

543-571: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Skip the second full tree check when enableContentCheck is already enabled.

enableContentCheck: true passes errorOnInvalidContent: true to createDocument, and JSON content already calls node.check() there. For that path, repairInvalidDoc(doc) adds another full-tree traversal before editing starts. Avoid the duplicate check for JSON content when the content option is enabled; for invalid JSON, this path is bypassed by the existing catch branch.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/core/src/Editor.ts` around lines 543 - 571, Avoid calling
repairInvalidDoc for JSON content when enableContentCheck is enabled, since
createDocument already validates it with node.check(). Update the document
initialization flow around repairInvalidDoc to bypass this redundant traversal
while preserving the existing catch behavior for invalid JSON and repair logic
for other content paths.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/core/__tests__/repairNode.spec.ts`:
- Around line 1-207: Add unit coverage in
packages/core/__tests__/repairNode.spec.ts (lines 1-207) using a node type whose
required content cannot be satisfied by fillBefore, and assert repairNode
returns null. Add integration coverage in
packages/core/__tests__/onContentError.spec.ts (lines 286-341) with
unrecoverable content, asserting the Editor falls back to an empty document via
schema.topNodeType.createAndFill() instead of throwing.

In `@packages/core/src/Editor.ts`:
- Around line 543-571: Avoid calling repairInvalidDoc for JSON content when
enableContentCheck is enabled, since createDocument already validates it with
node.check(). Update the document initialization flow around repairInvalidDoc to
bypass this redundant traversal while preserving the existing catch behavior for
invalid JSON and repair logic for other content paths.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: ea26df1d-016f-4bb4-9092-57efb89ae200

📥 Commits

Reviewing files that changed from the base of the PR and between 5158212 and ee8de71.

📒 Files selected for processing (6)
  • .changeset/2026-07-30-repair-invalid-initial-content.md
  • packages/core/__tests__/onContentError.spec.ts
  • packages/core/__tests__/repairNode.spec.ts
  • packages/core/src/Editor.ts
  • packages/core/src/helpers/index.ts
  • packages/core/src/helpers/repairNode.ts

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

Labels

area: core Core editor engine and ProseMirror integration complexity: expert Deep domain knowledge required, high risk impact: medium Affects some users or workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant