8.2.8: _parse_extended_key crashes the input thread on Kitty colon sub-parameters (key:shifted:base, mods:event) — ValueError in int(), app panics

Open Beginner friendly
#6,746 3 comments 0 reactions 0 assignees View on GitHub

@NavpreetSSidhu is already working on this.

Since Oct 29, 2026.

  • #1 by @NavpreetSSidhu — open

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
74/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
python
Domain
cli, testing

Research direction

Start with XTermParser._parse_extended_key in textual/_xterm_parser.py and the _re_extended_key pattern; the repro sequences in the issue show which colon parameters trigger ValueError. Check existing parser tests for extended Kitty keys, then verify that shifted/base key and event type parameters no longer crash parsing while the ordinary sequence still parses.

Written by the indexing model from the issue text.

Description

Have you checked closed issues? Yes. Have you checked against the most recent version of Textual? Yes, 8.2.8 (and main as of 2026-10-26, where _parse_extended_key is unchanged).

The bug

XTermParser._parse_extended_key (introduced in 8.2.8 by "Fixed parsing Kitty extended keys with multiple codepoints") matches a Kitty key report with

_re_extended_key = re.compile(r"\x1b\[((?:[\d:]*;?){2,3})([u~ABCDEFHPQRS])")

which admits colon sub-parameters in every field, then does

codepoint_str, modifiers_str, text_str, *_ = codes.split(";") + ["", "", ""]
codepoint = int(codepoint_str or "1")
modifiers = int(modifiers_str or "0")

Only text_str goes through _parse_colon_codepoints. A report whose first field carries the shifted/base alternates (CSI 97:65;2;65u, flag 4) or whose second field carries an event type (CSI 57441;1:3u, flag 2) raises

ValueError: invalid literal for int() with base 10: '97:65'

inside the input thread. LinuxDriver._run_input_thread catches it and schedules App.panic, so the app exits with a traceback, and every byte after it (keys, mouse, in-band resize reports) is lost.

8.2.7 (and 8.0.2) parse the same bytes without error. Textual itself requests flags 1+8+16, so a conforming terminal does not send these forms unprompted, but the protocol is a stack: any earlier program in the same terminal that pushed flags 2 or 4 and did not pop them, or a terminal that reports alternates/events regardless, kills the Textual app on the first such key.

Repro

Feed the parser directly, no terminal needed:

from textual._xterm_parser import XTermParser
p = XTermParser(debug=False)
for seq in ("\x1b[97;1;97u", "\x1b[97:65;2;65u", "\x1b[57441;1:3u"):
    try:
        print(seq.encode(), "->", p._parse_extended_key(seq))
    except ValueError as e:
        print(seq.encode(), "-> ValueError:", e)

8.2.8:

b'\x1b[97;1;97u' -> [Key(key='a', character='a')]
b'\x1b[97:65;2;65u' -> ValueError: invalid literal for int() with base 10: '97:65'
b'\x1b[57441;1:3u' -> ValueError: invalid literal for int() with base 10: '1:3'

End to end (pty, mode-2048 terminal emulated, the sequence written before a resize): the app observes no further resizes and its stdout carries the Rich traceback ending in that ValueError; 8.2.7 observes every resize.

Suggested fix

Split the first two fields on : before int() — codepoint = int(codepoint_str.partition(":")[0] or "1"), modifiers = int(modifiers_str.partition(":")[0] or "0") — and either keep the alternates/event type (the Kitty spec: field 1 is unicode-key-code[:shifted-key[:base-layout-key]], field 2 is modifiers[:event-type]) or drop them. Also worth considering: _run_input_thread turning a parser exception into a panic means one malformed report ends the app; logging and skipping the sequence would be the gentler failure.

Environment: Linux, Python 3.13.16, ghostty 1.3.1 (which does not send these forms under flags 25; found while bisecting a different resize problem).

Dominant language
Python
Stars
37.6k
Forks
1.4k
PR merge metrics
No merged PRs in 30d

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from Textualize/textual

All issues in Textualize/textual

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.