Description
On native Wayland, typing at normal speed in the rich composer body drops
characters and produces runaway key repeats. A held-or-repeating key free-runs,
so “cool” becomes “cooooooooooooooooooo…”, and unrelated keystrokes typed
during the runaway are lost entirely.
It affects all accounts and all messages, new drafts and replies alike, so it is
not account- or message-specific.
It is specific to the rich composer body. In the same window and the same
process:
- Subject field (a plain
<input>) - unaffected - Plaintext composer (
core.composing.html = false, a<textarea>) - unaffected - Rich composer body (Slate
contenteditable) - affected
Running Mailspring on XWayland (--ozone-platform=x11) avoids it completely.
That is the workaround I am using.
Example, typed as one sentence at a steady ~60ms per key:
sent: yeah cool, the ACMI thing is also free and we need to see it soon
received: yeah cooooooooooooooooooooooooooooooooooo is also free an
To Reproduce…
Steps to reproduce the behavior:
- Run Mailspring on a native Wayland session (Hyprland here) - confirm it is not
on XWayland. - Click Compose to open a new message.
- Click into the message body (the rich composer).
- Type a sentence at a normal fast pace, roughly 60ms between keystrokes.
- See characters dropped, and one character repeat dozens of times.
It does not reproduce on every single keystroke, but it reproduced in
approximately 25 of 25 attempts for a full sentence. The character that runs
away varies between attempts.
To reproduce deterministically, drive real key events through the compositor:
wtype -d 60 'yeah cool, the ACMI thing is also free and we need to see it soon'
Important: this does not reproduce through Chrome DevTools Protocol
Input.dispatchKeyEvent, because that injects events past the Wayland input
path. Using CDP, 1000 keystrokes over 25 seconds arrived perfectly (0.9ms median
lag, zero long tasks). The bug only appears with real compositor input.
Expected Behavior
Text typed into the rich composer appears exactly as typed, with no dropped or
duplicated characters, the same as the Subject field and the plaintext composer.
Screenshots
Not attached. The text sample above shows the failure exactly - a single
character repeating for dozens of characters mid-sentence, with the text that
should have been typed during the runaway missing.
Setup
OS and Version: Omarchy 4.0.0.alpha (Arch Linux), kernel 7.2.3-arch1-3, Hyprland
v0.56.2, native Wayland session
Installation Method: locally built pacman package from the official upstream
mailspring-1.24.0-0.1.x86_64.rpm, keeping the bundled Electron. Previously the
AUR mailspring-bin package, which uses a system Electron.
Mailspring Version: 1.24.0 (bundled Electron 44.3.0 / Chromium 152.0.7977.78).
Also reproduced on 1.23.0 (system Electron 41.10.7 / Chromium 146.0.7680.216).
Additional Context
Where the loss happens. Instrumenting the composer renderer during a failing
run, with real compositor input:
| Measure | Real Wayland input | Via CDP (bypasses Wayland) |
|---|---|---|
| Median keydown lag | 22.6 ms | 0.9 ms |
| Max keydown lag | 64.9 ms | 2.5 ms |
| Keystrokes reaching renderer | 50 of 65 sent | 1000 of 1000 |
| Renderer long tasks | one, 68ms total | none |
Fifteen keystrokes never reached the renderer at all, and the renderer was
blocked for only 68ms across the whole run. So this is not Slate’s JavaScript
being slow - the events are lost upstream of the renderer, in the browser
process input path. Chromium also flagged many of the delivered keydowns as
repeat: true (129 in one run), meaning its auto-repeat timer was firing even
though the compositor sent discrete press/release pairs.
Controls, all given byte-identical input, all clean. These are what make me
fairly confident this is a Mailspring issue and not a general Electron or
Chromium-on-Wayland one:
| Control | Result |
|---|---|
foot terminal (non-Chromium) |
received the sentence byte-perfect |
| Google Chrome 153, textarea, native Wayland | clean, 0 repeat-flagged keydowns |
Bare Electron 41 app (Chromium 146.0.7680.216, the identical build Mailspring 1.23 used) with a focused contenteditable, native Wayland |
5/5 clean, 0 repeats |
| Mailspring Subject field (plain input) | 3/3 clean |
| Mailspring plaintext composer (textarea) | 5/5 clean |
| Mailspring rich composer | mangled every run |
The bare-Electron control is the key one: a plain contenteditable on the exact
same Chromium build is fine, so it takes Mailspring’s real composer to trigger
this.
Ruled out, each tested rather than assumed:
- Theme - reproduced on stock
ui-dark, not only my custom theme - Third-party plugin - reproduced with it inactive; it is main-window only anyway
- Grammar check - reproduced with
core.composing.grammarCheck = false - Spellcheck - A/B of 5 runs each way, 0/5 clean both ways (64 vs 89 repeats)
- First-keystroke draft-session setup - a warm-up keystroke first did not help
- Compositor key repeat settings - raising
repeat_delayfrom 250ms to 600ms
reduced the repeats but the dropped characters continued - Electron/Chromium version - reproduced on Electron 41/Chromium 146 and on
Electron 44/Chromium 152
A theory I want to explicitly withdraw, in case it looks tempting:
src/components/composer-editor/patch-chrome-ime.js has no platform guard, and
its substitutionsPanelMayBeOpen logic dispatches a synthetic Backspace after
any repeating key is released - which looks like an exact match for the dropped
characters on Linux. I instrumented it and counted zero synthetic backspace
injections during a failing run. It is not the cause. It may still be worth a
platform guard on its own merits, but it is not this bug.
Caveat on methodology. wtype uploads its own xkb keymap per character,
which a physical keyboard does not do, so the synthetic drop and repeat counts
may overstate the real-world rate. Two things support the result anyway: every
control above received identical input and was clean, and I independently
confirmed both the bug and the XWayland workaround by typing on a physical
keyboard.
Workaround for anyone else hitting this: add --ozone-platform=x11 to
~/.config/mailspring-flags.conf to run on XWayland.