You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Bug: Editor freezes when pasting text copied from websites into a nested editor state #7635
Copy text from somewhere in github.com (e.g. some random text from a GitHub issue)
Paste it into the image caption in Lexical Playground (note: image caption is a nested editor state)
The editor is now frozen
Demo video:
20250620-1549-37.0186866.mp4
The current behavior
Pasting text from websites into the editor causes it to freeze.
The expected behavior
Pasting text from websites doesn't cause the editor to freeze.
Impact of fix
This is a very severe bug. We use nested editor states in several custom Lexical plugins. Our users commonly paste content into them, which causes the editor to freeze and become unusable.
changed the title [-]Bug: Editor freezes when pasting text copied from websites[/-][+]Bug: Editor freezes when pasting text copied from websites into a nested editor state[/+]on Jun 20, 2025
I'm afraid you'll have to be more precise about what content exactly causes this issue. I could not reproduce given the instructions. The video attachment also isn't loading. A tool like Clipboard Inspector will allow you to inspect exactly what content is on your pasteboard, the text/html data would be relevant here.
I'm afraid you'll have to be more precise about what content exactly causes this issue. I could not reproduce given the instructions. The video attachment also isn't loading. A tool like Clipboard Inspector will allow you to inspect exactly what content is on your pasteboard, the text/html data would be relevant here.
Okay, so the description of this bug "Editor is now frozen" is the part that wasn't clear until seeing the video. "Editor is now frozen" means that the caption is still completely editable, the browser is still interactive, but the parent editor no longer works correctly. Normally when people say that the editor is frozen there is some sort of infinite loop or exception that occurs on any interaction.
A quick look into this issue it seems that the problem is that the way Lexical handles commands with nested editors is a bit error prone.
$insertGeneratedNodes dispatches SELECTION_INSERT_CLIPBOARD_NODES_COMMAND to see if something wants to handle the paste, and if not, it will call selection.insertNodes
The payload of SELECTION_INSERT_CLIPBOARD_NODES_COMMAND has a selection property which is a BaseSelection.
The BaseSelection is implicitly bound to the editor it was created in (the keys make no sense in any other editor, will always be missing)
triggerCommandListeners (what editor.dispatchCommand does under the hood) dispatches the command to the intended editor and all of its ancestors until some listener returns true
The only thing listening to this command in the playground is the table plugin that will not do anything in the image caption (it's not installed there) but will get called from the parent editor
When the parent editor gets this command it tries to use the selection and fails, but in a quiet way because of lexical's "helpful" error suppression
I think at this point the selection isn't restored correctly in the parent editor and it gets broken forever for that reason (detecting and fixing this broken state can/should be separately fixable)
There are a couple ways to fix the root cause:
The targeted "just fix the bug" way without breaking changes is to have these table helpers check the second argument in their command listeners to see if the editor is the one that the listener was registered to and if not then exit early (return false in this case because $insertGeneratedNodes will not work correctly if return true happens). This leaves the opportunity for other code (not distributed in lexical) to have this same bug. [lexical-table] Bug Fix: Ignore SELECTION_INSERT_CLIPBOARD_NODES_COMMAND dispatched from child editors #7637 implements this minimal fix (will write tests before moving it out of draft state).
The more robust fix would be to add some editor priority listener that does the selection.insertNodes and returns true (preventing dispatch to the parent) instead of having $insertGeneratedNodes inspect the return value (which requires dispatching through all parent editors as-is since false is the expected value). This would have to be part of the RichText/PlainText plugins since there's no "clipboard" plugin for this to belong to, and the priority level of the listener in registerTablePlugin would have to be bumped up from editor to low because commands are evaluated in priority descending and then registration ascending order. This is backwards incompatible mostly due to the priority issue, since we don't know if people have written their own editor priority handlers for this command and if they have they would be rendered useless by this strategy.
Remove command forwarding altogether and have this be opt-in (e.g. an editor priority listener could explicitly re-dispatch to the parent editor if that's useful for a particular command). This is obviously more of a breaking change, but would make this entire class of bug opt-in.
Activity
[-]Bug: Editor freezes when pasting text copied from websites[/-][+]Bug: Editor freezes when pasting text copied from websites into a nested editor state[/+]etrepum commentedon Jun 20, 2025
Are you able to reproduce this anywhere other than an image caption?
etrepum commentedon Jun 20, 2025
I'm afraid you'll have to be more precise about what content exactly causes this issue. I could not reproduce given the instructions. The video attachment also isn't loading. A tool like Clipboard Inspector will allow you to inspect exactly what content is on your pasteboard, the text/html data would be relevant here.
johnnyoshika commentedon Jun 20, 2025
I can reproduce this anywhere where there's an embedded editor state. We've built 8 plugins that use that and the same problem happens in all of them.
johnnyoshika commentedon Jun 20, 2025
I've uploaded the demo video here: https://drive.google.com/file/d/14w4iTCAdoZlQSutbSHe0A0tYY77RElmk/view?usp=sharing
I'll also try Clipboard Inspector to inspect what's in the clipboard.
johnnyoshika commentedon Jun 20, 2025
@etrepum I copied some text from your message above and used Clipboard Inspector. It shows this:
text/plain
text/html
Pasting this into the caption of the image (after adding text then deleting it) freezes the editor.
etrepum commentedon Jun 21, 2025
Okay, so the description of this bug "Editor is now frozen" is the part that wasn't clear until seeing the video. "Editor is now frozen" means that the caption is still completely editable, the browser is still interactive, but the parent editor no longer works correctly. Normally when people say that the editor is frozen there is some sort of infinite loop or exception that occurs on any interaction.
etrepum commentedon Jun 21, 2025
A quick look into this issue it seems that the problem is that the way Lexical handles commands with nested editors is a bit error prone.
$insertGeneratedNodesdispatchesSELECTION_INSERT_CLIPBOARD_NODES_COMMANDto see if something wants to handle the paste, and if not, it will callselection.insertNodesSELECTION_INSERT_CLIPBOARD_NODES_COMMANDhas aselectionproperty which is a BaseSelection.triggerCommandListeners(whateditor.dispatchCommanddoes under the hood) dispatches the command to the intended editor and all of its ancestors until some listener returnstrueThere are a couple ways to fix the root cause:
$insertGeneratedNodeswill not work correctly ifreturn truehappens). This leaves the opportunity for other code (not distributed in lexical) to have this same bug. [lexical-table] Bug Fix: Ignore SELECTION_INSERT_CLIPBOARD_NODES_COMMAND dispatched from child editors #7637 implements this minimal fix (will write tests before moving it out of draft state).selection.insertNodesand returns true (preventing dispatch to the parent) instead of having$insertGeneratedNodesinspect the return value (which requires dispatching through all parent editors as-is since false is the expected value). This would have to be part of the RichText/PlainText plugins since there's no "clipboard" plugin for this to belong to, and the priority level of the listener inregisterTablePluginwould have to be bumped up from editor to low because commands are evaluated in priority descending and then registration ascending order. This is backwards incompatible mostly due to the priority issue, since we don't know if people have written their own editor priority handlers for this command and if they have they would be rendered useless by this strategy.johnnyoshika commentedon Jun 22, 2025
@etrepum Thank you so much for this investigation and detailed explanation. Thank you also for the quick PR to fix this -> simply amazing!