Skip to content

Punctuation at 14th unique character position in Chromium/Electron #71

Open

Description

@JacksonKaunismaa

When typing into Chromium or Electron apps, if a punctuation character happens to be the 14th unique character in the string, wtype fails to type correctly.

# Focus a text field in Chromium/Vivaldi/any Electron app, then run:

wtype "abcdefghijklm?XYZ"   # 14 unique chars, ? is 14th
# Expected: abcdefghijklm?XYZ
# Actual:   abcdefghijklXYZ   (? interpreted as backspace, deletes 'm')

wtype "abcdefghijklmn?XYZ"  # 15 unique chars, ? is 15th
# Expected: abcdefghijklmn?XYZ
# Actual:   abcdefghijklmn?XYZ  (works correctly)

This seems to happen on specifically Chromium / electron apps (e.g. Vivaldi or Brave browser, Joplin, etc).

I think this might be related to keycode 22 being BackSpace in the standard evdev keymap:
If wtype assigns keycodes starting at 9, the 14th unique character would get keycode 22. Maybe Chromium is interpreting the raw keycode as BackSpace for certain characters despite the custom keymap?

Environment: Arch, Hyprland 0.52.1, wtype 0.4-2, Wayland 1.24.0

Activity

  1. cilki commented on Feb 21, 2026

    @cilki

    Took me forever to figure this one out. I use wtype for passwords and certain ones simply would not work. Now I know...

    I don't find this to be limited to electron apps, though. It happens in my terminal as well.

  2. hoblin commented on Aug 15, 2026

    @hoblin

    [claude-opus-5] on behalf of hoblin

    Confirming this on Hyprland 0.56.2 / wtype 0.4, with measurements suggesting the scope is wider than the title. Posting in case it helps anyone arriving from one of the other reports.

    It is positional, not character-specific

    The 33-letter Cyrillic alphabet, every letter distinct, so position == assigned keycode:

    sent: абвгдеёжзийклмнопрстуфхцчшщъыьэюя
    got:  абвгдеёжзийкл<TAB>опрстуфхцчшщъыьэюя
    

    м (index 13 → keycode 22) vanishes; н (index 14 → keycode 23) arrives as a literal Tab.

    The same alphabet reversed moves the damage to different letters at the same indices:

    sent: яюэьыъщшчцхфутсрпонмлкйизжёедгвба
    got:  яюэьыъщшчцхфу<TAB>рпонмлкйизжёедгвба
    

    Now т and с. So it is not "punctuation", and not any particular character — it is whichever keycode a character is assigned.

    Indexing is per character, not per byte: н is at character index 14 but byte offset 28, and it produced a Tab (keycode 23). Byte indexing would have put it on keycode 37 (Control_L), which emits nothing.

    Two distinct failure bands

    Holding the client fixed and varying only the script, all three strings arranged so indices 13, 14 and 28 map to keycodes 22, 23 and 37:

    script kc 22 kc 23 kc 37
    ASCII n ✅ o ✅ C ❌
    Cyrillic м ❌ н ❌ ы ❌
    Greek ξ ❌ ο ❌ Ε ❌
    • Keycodes 22/23 (BackSpace, Tab) — corrupt non-ASCII only; ASCII passes through
    • Keycodes 37, 50, 67–75 (Control_L, Shift_L, F1–F9) — corrupt everything, ASCII included

    I do not have an explanation for why the low band spares ASCII and would rather flag it than guess. A plausible direction is keysym representation — ASCII lands in 0x20–0x7E while non-ASCII becomes 0x01000000 + codepoint — but I have not verified that.

    Not limited to Chromium/Electron

    Same input, different clients, different collisions:

    keycode ghostty foot Obsidian (Electron)
    22 broken broken broken
    23 broken broken fine
    37 / 64 fine fine broken
    68–75 broken broken F9 only

    Which collisions bite depends on which keys a client special-cases by scancode rather than by keysym. (This matches @cilki's comment above that it is not Electron-only.)

    Damage can exceed the dropped character

    In Obsidian, keycode 22 acts as a real BackSpace and deletes the preceding character:

    sent: ...звучит как что-то очень нетехническое и от человека ... как всё работает.
    got:  ...учит как что-то очень нетеническое и от челека ... каксё работает.
    

    звучит → учит, человека → челека, как всё → каксё. In ghostty the same keycode is swallowed silently. Either way there is no error.

    ⚠️ Beyond ~keycode 80 this stops being a text bug

    A string with 151 distinct characters reaches keycode 159, which is where Super, Print Screen, the media keys and the XF86 keys live. Running that did not merely lose characters — it executed those keys: opened a calculator, took a screenshot, and changed the mode of the focused application.

    So with enough distinct characters, wtype can trigger arbitrary system actions. Anyone reproducing this should stay inside the printable range.

    Reproduction

    wtype 'абвгдеёжзийклмнопрстуфхцчшщъыьэюя'   # indices 13,14 → keycodes 22,23
    wtype 'αβγδεζηθικλμνξοπρστυφχψωΑΒΓΔΕΖΗΘΙ'   # same indices, different script

    Both reproduced identically across 11/11 runs. A short pure-ASCII string does not reproduce it — it has to reach keycode 37 or above.

    Not expecting a fix here

    Last commit on this repo is 2022-01-27, so this is documentation for the next person searching rather than a fix request. The same root cause appears to explain #72, #46, #58, #40, #31, #37, #38 and #76 (!/* as first character → keycode 9 → Escape). Both #4 ("Use libxkbcommon to generate the keymap string") and #5 ("Use input-method if available") point at the real fix.

    Workaround

    Any keycode-based approach inherits this. What works reliably is not typing at all: put the text on the clipboard as UTF-8 and issue a single paste — on Hyprland that can be done through the compositor's own key injection, with no virtual keyboard involved.

  3. cilki commented on Aug 15, 2026

    @cilki

    @hoblin We know what the problem is. Maybe you could have generated an MR instead?

  4. hoblin commented on Aug 16, 2026

    @hoblin

    @hoblin We know what the problem is. Maybe you could have generated an MR instead?

    Sorry, I'm not quite sure what MR is 🙃

  5. tijszwinkels commented on Sep 21, 2026

    @tijszwinkels
    Contributor

    I reproduced this with wtype 0.4-2 on Arch, using Hyprland 0.56.2 and Chromium 153.0.8010.36 on Wayland:

    wtype -d 1 -- "let's just keep it open."
    # Actual: let's just keep it ope
    

    The browser records these events when wtype reaches the period:

    keydown:     key=".", code="Backspace", keyCode=8
    beforeinput: inputType="deleteContentBackward"
    input:       inputType="deleteContentBackward"
    

    So the final characters aren’t simply getting lost: the n is typed successfully, then the period acts as Backspace and deletes it.

    This confirms the keycode-22 explanation above. In wtype v0.4, each different character gets the next available keycode, starting at 9. Repeated characters reuse their assigned code. The period is the 14th different character in this sentence, so it gets keycode 22—the code normally used for the physical Backspace key.

    A fix is to assign text only to keycodes normally used for printable characters, skipping keys such as Backspace, Escape and Tab. If there are more distinct characters than available codes, wtype can reuse those codes by updating the character map between completed keypresses.

    MR forthcoming.

  6. tijszwinkels commented on Sep 21, 2026

    @tijszwinkels
    Contributor

    Fixed in #77

  7. ayagmar commented on Oct 14, 2026

    @ayagmar

    Same thing here today, typing into Discord (Vesktop) on niri with wtype 0.4. A part of text that started with a space lost every space in it (slot 1 is Escape), and a period that was the 14th distinct character deleted the letter before it (slot 14 is Backspace). Letters on those slots come through fine. Emoji arrive cut to 16 bits with any keycode (U+1F916 shows up as U+F916), so that part is Chromium's keysym handling rather than the keymap. Pasting through the clipboard works around all of it.

  8. added a commit that references this issue on Oct 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions