Skip to content

Suggesting mode: after Enter + Backspace inside your own pending insertion, typing over the paragraph is rejected, and on 2.28.0 the editor stops taking input #4119

Open

Description

@ZacharyHampton

Versions

  • superdoc 2.28.0 / @superdoc/docx-engine 0.27.0: keystrokes rejected, then the render
    worker fails and every later keystroke is dropped.
  • superdoc 2.26.0 / @superdoc/docx-engine 0.25.0: the same first keystrokes are rejected,
    but the editor recovers and the rest of the typing lands.
  • Measured 2026-10-30 in Bun 1.4.4 under happy-dom, single editor, no collaboration provider
    (the same result inside a y-websocket room). Keys were sent to the editor's own input
    element (textarea[data-v2-ime-host]) as keydown (Enter, Backspace) and as
    beforeinput with inputType: 'insertText', which is how the host receives typing. The
    selection was placed with editor.authoring.setSelectionTarget. This was not run in a real
    browser.

Summary

In Suggesting mode, the user rewrites a paragraph as a suggestion, puts the caret inside their
own inserted text, presses Enter, and then presses Backspace to join the paragraph again. The
paragraph looks unchanged, but the user's insertion is now two tracked changes split at the
join point. When the user then selects the whole paragraph and types over it:

  • the first keystrokes come back as edit-rejected ("This edit couldn't be completed. Adjust
    the selection and try again."), and their characters are lost (2.26.0 and 2.28.0);
  • on 2.28.0 the next keystroke ends in a render-worker failure: Worker authored annotations have unproved canonical ownership: Authored carrier is absent from its current review group (internalCode: worker-dom.failed, diagnosticCode: RENDER_ERROR). The host removes
    its input textarea, setSelectionTarget returns { ok: false, reason: 'host-not-ready' },
    and every later keystroke is dropped.

Without the Enter + Backspace step, the same type-over works on both versions.

Steps

One .docx with one paragraph: Water the beans at dawn.

import { SuperDoc } from 'superdoc'

const sd = new SuperDoc({
  selector: '#editor',
  document: { type: 'docx', url: './sample.docx' },
  documentMode: 'suggesting',
  user: { name: 'Gardener', email: 'gardener@example.com' },
  onException: (e) => console.log('exception', e.code ?? e.internalCode, e.error?.message ?? e.message),
})
// once ready:
const ed = sd.activeEditor
const blockId = (await ed.doc.blocks.list({ includeText: true })).blocks[0].nodeId
const at = (offset) => ({ kind: 'text', blockId, offset, story: { kind: 'story', storyType: 'body' } })
const select = (a, b) =>
  ed.authoring.setSelectionTarget({ target: { kind: 'selection', start: at(a), end: at(b) }, focus: true })
const ime = () => document.querySelector('textarea[data-v2-ime-host]')
const type = async (s) => {
  for (const ch of s) {
    ime()?.dispatchEvent(new InputEvent('beforeinput', { inputType: 'insertText', data: ch, cancelable: true, bubbles: true }))
    await new Promise((r) => setTimeout(r, 40))
  }
}
const key = (k) => ime().dispatchEvent(new KeyboardEvent('keydown', { key: k, bubbles: true, cancelable: true }))
  1. await select(0, 24); await type('Plant peas early and often'). The paragraph reads
    Plant peas early and often as one suggested replacement.
  2. await select(15, 15); key('Enter'), wait, then key('Backspace'). The text is the same
    again, but doc.trackChanges.list() now reports two changes:
    replacement "Plant peas earl" and insertion "y and often".
  3. await select(0, 26); await type('Sow beans late').
  4. await select(len, len); await type(' in rows'), where len is the paragraph's length.

Expected

Step 3 replaces the paragraph with the suggestion Sow beans late, as it does when step 2 is
skipped. Step 4 appends in rows.

Actual

2.26.0 / 0.25.0 2.28.0 / 0.27.0
step 3, edit-rejected exceptions 3 (S, o, w lost) 3 (S, o, w lost)
step 3, render failure none worker-dom.failed: "Worker authored annotations have unproved canonical ownership: Authored carrier is absent from its current review group"
paragraph after step 3 beans late (only the space landed)
textarea[data-v2-ime-host] after step 3 present removed
step 4 setSelectionTarget { ok: true } { ok: false, reason: 'host-not-ready' }
paragraph after step 4 beans late in rows (all 8 keys dropped)

Controls, same build:

  • Steps 1, 3 and 4 without step 2: Sow beans late in rows on both versions, no exceptions.
  • Enter + Backspace in plain text (no pending insertion), then type-over: correct on both
    versions.
  • A second user does step 3 instead of the author: no rejection on either version. Their
    type-over produces two tracked deletions split at the join point (Plant peas earl,
    y and often) and two empty insertions (insertion "").

Activity

  1. ghost added
    status: triageNew report awaiting initial assessment.
    on Oct 30, 2026
  2. ghost added theissue type on Oct 30, 2026
  3. self-assigned this
    on Oct 30, 2026
  4. caio-pizzol commented on Oct 30, 2026

    @caio-pizzol
    Contributor

    Hey Zachary, thanks for the really detailed report. The exact steps and controls made this quick to track down.

    I reproduced it on our latest main in Chrome, so it isn't a happy-dom quirk. It turns out to be three bugs stacked on top of each other:

    1. Enter + Backspace doesn't rejoin your suggestion. The text looks the same, but it stays split into two suggestions. That's why trackChanges.list() shows two changes.
    2. Typing over both halves gets rejected. If your selection covers both halves, the editor refuses the edit. That's the edit-rejected errors and the lost S, o, w.
    3. The editor stops when a key does get through. The space is accepted, but the renderer loses track of one of the suggestions and stops instead of recovering. That's the worker-dom.failed error, and why every key after it is dropped.

    We're tracking all three on our side. I'll update this issue when fixes land.

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

Metadata

Metadata

Assignees

Labels

status: triageNew report awaiting initial assessment.

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions