Repository navigation
[Bug]: Terminal paste lands twice in Chromium browsers (inverse of the #8457 race) #13346
Description
Activity
Confirmed on
main@894d33419d. Checked from source only. The shortcut/native paste race inGhosttyTerminalSurfaceis unchanged. This is the inverse ordering left open when #8457 was closed, not a duplicate of #11955.What happens
The paste shortcut starts
navigator.clipboard.readText()and does not cancel the browser's paste:onPastealways writes non-emptytext/plainto the PTY. The token bump only invalidates a read that has not settled yet:Native-paste-first is covered:
onPastemovespasteShortcutToken, and the read bails on the mismatch. Read-first is not. The read delivers, moves the token, and the paste event that follows still callsonData. That is the hole named when #8457 was closed: the async clipboard read can deliver before a later native paste event. The comment above the read still says the native event always claims the token first. That holds only when the paste event runs inside the keydown default action, before the clipboard task.Shortcuts on this path are Cmd+V on macOS, and Ctrl+Shift+V or Shift+Insert elsewhere (
isTerminalPasteShortcut, lines 386–396). Plain Ctrl+V on Linux and Windows is still quoted-insert, unless #9201 lands. Desktop uses this same surface, so it is not specific to the hosted web client. #11955 was a second insertion from the Electron Paste as Text accelerator, and that one is already fixed. Context-menu paste goes throughpasteFromClipboard, which claims the token before its own read.Fix
de8ff28is the right shape: the read records the text it sent, andonPastedrops one equal payload. A keydown clears the record so a later gesture is not eaten. The native-first path stays on the token. @r4iju please open that PR. We will not open a competing one.Two gaps to close on it:
- The record survives the gesture. A shortcut whose browser never fires a paste event (the case the read exists for) leaves
shortcutPasteReadTextset until the next keydown. Edit → Paste of that same string is anonPastewith no keydown, so it is dropped. The terminal context menu does not hit this; it usespasteFromClipboard. Also clear the record on the shortcut's keyup. The clipboard spec dispatches that keyboard paste before keyup, so the duplicate from this bug is still dropped. - The new test never flushes the second read. After the second Cmd+V and its native paste, await the clipboard promise and assert
onDatais still at 2. As written, a second read that also delivers can resolve after the assertion.
- The record survives the gesture. A shortcut whose browser never fires a paste event (the case the read exists for) leaves
- addedacceptedfeature request acceptedfeature request acceptedbugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
on Sep 24, 2026 The fix for this is in #13357. It was closed for missing interaction evidence, and has since been updated with before/after recordings in its Verification section (macOS 27, Brave installed web app): a shortcut paste now lands once, and a later same-text Edit → Paste still lands after keyup and after blur. The branch is unchanged and still merges cleanly into
main. Could #13357 be reconsidered?
In the web client (Brave, over the network), pasting into the integrated terminal often sends the text twice. I hit it constantly.
The paste shortcut starts
navigator.clipboard.readText()and keeps the nativepasteevent as a fallback (apps/web/src/terminal/ghostty/surface.ts).pasteShortcutTokenonly handles the native event arriving first. If the read resolves first, the native paste that follows is also sent. This is the "remaining inverse ordering" noted when #8457 was closed.I have a small fix with a regression test in my fork: the read records the text it delivered, and the matching native paste from the same gesture is dropped. Any keydown clears the record, so a later paste is never swallowed. Reference commit: r4iju@de8ff28
Happy to open a PR if you want it.