Skip to content

[lexical][@lexical/link] Bug Fix: Fix infinite transform loop in AutoLinkPlugin#8070

Merged
etrepum merged 7 commits intofacebook:mainfrom
aldoprogrammer:fix/autolink-infinite-transform
Jan 13, 2026
Merged

[lexical][@lexical/link] Bug Fix: Fix infinite transform loop in AutoLinkPlugin#8070
etrepum merged 7 commits intofacebook:mainfrom
aldoprogrammer:fix/autolink-infinite-transform

Conversation

@aldoprogrammer
Copy link
Copy Markdown
Contributor

Description

Fixes infinite transform loop in AutoLinkPlugin when initializing content with patterns like #1234.Another where the matcher matches #1234 but the text continues with a period (.). The period after the match causes boundary validation to fail, which triggers link unwrapping, which re-triggers the transform, creating an infinite loop.

Current Behavior

When initializing editor content with text like #1234.Another where #1234 matches a link matcher but is followed by a period, the AutoLink transform enters an infinite loop:

  • Transform matches #1234 and creates an AutoLinkNode
  • Boundary validation fails because .Another follows the match
  • Link is unwrapped, triggering another transform
  • Process repeats indefinitely
  • Error: "One or more transforms are endlessly triggering additional transforms"

Changes in this PR

Made the registerAutoLink transform idempotent by adding guards to prevent re-processing nodes that are already part of an AutoLinkNode:

  1. $handleLinkCreation: Added early return if any input node is already linked, and skip creating a new link if matching nodes are already part of an AutoLinkNode.

  2. handleBadNeighbors: Added early return if textNode is already part of an AutoLinkNode, and added validation checks before appending to siblings to ensure nodes are still siblings and the combined text would still form a valid link.

  3. registerAutoLink transform: Added check to skip processing if textNode is already part of an AutoLinkNode.

These changes ensure the transform only processes nodes that actually need link creation/editing, preventing infinite loops while maintaining correct link behavior.

Testing

  • Added unit test registerAutoLink does not cause infinite transform loop with #1234.Another that reproduces the issue and verifies the fix
  • All existing tests pass (68/68)

Closes #7646

@vercel
Copy link
Copy Markdown

vercel bot commented Jan 11, 2026

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

Project Deployment Review Updated (UTC)
lexical Ready Ready Preview, Comment Jan 13, 2026 4:48am
lexical-playground Ready Ready Preview, Comment Jan 13, 2026 4:48am

@meta-cla meta-cla bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jan 11, 2026
@etrepum
Copy link
Copy Markdown
Collaborator

etrepum commented Jan 11, 2026

It looks like this PR is failing e2e tests

@aldoprogrammer
Copy link
Copy Markdown
Contributor Author

Ok, just fixed it

@etrepum etrepum added the extended-tests Run extended e2e tests on a PR label Jan 11, 2026
@aldoprogrammer
Copy link
Copy Markdown
Contributor Author

Plesae do check again, just push another one.

Copy link
Copy Markdown
Collaborator

@etrepum etrepum left a comment

Choose a reason for hiding this comment

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

This looks good but let's clean up the tests to use assert, should reduce a lot of the code

Comment on lines +68 to +74
const paragraph = root.getFirstChild();

// Verify paragraph exists (not empty root) and is an ElementNode
expect(paragraph).not.toBeNull();
expect($isParagraphNode(paragraph)).toBe(true);

const paragraphNode = paragraph as ParagraphNode;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

vitest has an assert function that lets you write expectations that will also refine the types so you don't need to do any casting

Suggested change
const paragraph = root.getFirstChild();
// Verify paragraph exists (not empty root) and is an ElementNode
expect(paragraph).not.toBeNull();
expect($isParagraphNode(paragraph)).toBe(true);
const paragraphNode = paragraph as ParagraphNode;
const paragraphNode = root.getFirstChild();
assert($isParagraphNode(paragraphNode), 'first root child must be a ParagraphNode');

@aldoprogrammer
Copy link
Copy Markdown
Contributor Author

oke cool, now another one pushed.

@etrepum etrepum added this pull request to the merge queue Jan 13, 2026
Merged via the queue into facebook:main with commit 21458a9 Jan 13, 2026
39 checks passed
@aldoprogrammer aldoprogrammer deleted the fix/autolink-infinite-transform branch January 22, 2026 23:38
@etrepum etrepum mentioned this pull request Jan 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. extended-tests Run extended e2e tests on a PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Pattern makes AutoLinkPlugin throw error: One or more transforms are endlessly triggering additional transforms

2 participants