Skip to content

Duplicated editors after crash with watchdog #601

Description

@FilipTokarski

Steps to reproduce:

  1. Download React integration from the builder
  2. Expose editor instance in window:
onReady={ editor => {
    window.editor = editor;
} }
  1. Run the app
  2. Open console and execute code to trigger error:
const root = editor.model.document.getRoot();

editor.model.change( writer => {
    const invalidElement = writer.createElement( 'invalid' );
    writer.append( invalidElement, root );
} );

Result

Editor crashes as expected. Now if you try typing something and click on the toolbar, another editor instance shows up. Also, if you keep clicking on it, it duplicates content on each click.

On some reports from customers the duplicated editor shows up immediately, without the need to type or click. However I was not able to reproduce it this way.

Screencast

Screen.Recording.2025-06-13.at.12.29.09.mov

Other info

Tested on:

  • CKEditor v 45.2.0
  • React integration v 9.5.0
  • React integration v 10.0.0-alpha.0
  • If you set disableWatchdog={true} then there are no duplicated editors

Activity

  1. added
    squad:coreIssue to be handled by the Core team.
    support:2An issue reported by a commercially licensed client.
    on Jun 13, 2025
  2. self-assigned this
    on Jun 16, 2025
  3. Mati365 commented on Jun 16, 2025

    @Mati365
    Member

    I briefly checked this case, and it doesn't seem directly related to the integration itself. The problem is that the editor doesn't properly destroy itself, and some leftover elements stay in the DOM.

    In this specific case, the editor changes its state from isFocused = true to isFocused = false, which triggers a change event and then throws an error about an invalid element.

    While we can't fix every possible exception that might happen during the destroy() process, we can at least detach unused editors from the DOM. They'll still stay in memory, but at least they won't be duplicated on the frontend.

  4. Mati365 commented on Jun 17, 2025

    @Mati365
    Member

    I managed to track down what’s actually going wrong here.

    The problem occurs when an invalid element is inserted into the editor model - something that isn’t registered in the schema. Interestingly, the editor doesn’t crash immediately. It stays mounted and looks fine at first, but it’s already in a broken state under the hood.

    The real crash happens later, typically during some internal interaction - for example, when the editor tries to update its state, or during destruction. At that point, it fires a change event, and because the model contains an invalid element, that event throws. This breaks the rest of the flow.

    If the crash happens during destruction:

    • onAfterDestroy is never called,
    • the editor’s UI is left behind in the DOM,
    • and Watchdog tries to restart the editor using the same invalid model,
    • which causes an immediate crash in the new instance too.

    Workaround

    What worked for me was wrapping the editor in a React Error Boundary and forcing a full remount of the component when a crash is caught. This way, even if the editor doesn’t clean up correctly, React unmounts everything and gives me a clean slate.

    Here’s a minimal example:

    // ...
    const [revision, setRevision] = useState(0);
    const onUpdateRevision = () => setRevision(prev => prev + 1);
    
    //..
    const dataRef = useRef('');
    const onHandleData = (...) => {
        dataRef.current = ...;
    };
    
    <CKEErrorBoundary onError={onUpdateRevision}>
        <CKEditor5
            key={revision}
            onChange={onHandleData}
            config={{ ...config, initialData: dataRef.current }}
        />
    </CKEErrorBoundary>

    This makes the editor recoverable, using the last known good data, and React handles cleanup of any leftover DOM from the broken instance.

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

Metadata

Metadata

Assignees

Labels

squad:coreIssue to be handled by the Core team.support:2An issue reported by a commercially licensed client.type:bug

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions