Repository navigation
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
Copy link
Copy link
Open
Labels
status: triageNew report awaiting initial assessment.New report awaiting initial assessment.
Description
Versions
superdoc2.28.0 /@superdoc/docx-engine0.27.0: keystrokes rejected, then the render
worker fails and every later keystroke is dropped.superdoc2.26.0 /@superdoc/docx-engine0.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]) askeydown(Enter, Backspace) and as
beforeinputwithinputType: 'insertText', which is how the host receives typing. The
selection was placed witheditor.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,setSelectionTargetreturns{ 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 }))await select(0, 24); await type('Plant peas early and often'). The paragraph reads
Plant peas early and oftenas one suggested replacement.await select(15, 15); key('Enter'), wait, thenkey('Backspace'). The text is the same
again, butdoc.trackChanges.list()now reports two changes:
replacement "Plant peas earl"andinsertion "y and often".await select(0, 26); await type('Sow beans late').await select(len, len); await type(' in rows'), wherelenis 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 rowson 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 "").
Metadata
Metadata
Assignees
Labels
status: triageNew report awaiting initial assessment.New report awaiting initial assessment.
Activity
caio-pizzol commentedon Oct 30, 2026
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
mainin Chrome, so it isn't a happy-dom quirk. It turns out to be three bugs stacked on top of each other:trackChanges.list()shows two changes.edit-rejectederrors and the lostS,o,w.worker-dom.failederror, and why every key after it is dropped.We're tracking all three on our side. I'll update this issue when fixes land.