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
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 74/100
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
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from Textualize/textual
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Textualize/textual#6753 · 1 comment ·
-
text boxes never stop listeningMay be free again A pull request for this issue was closed without being merged. Open
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
Textualize/textual#6744 · 3 comments ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Textualize/textual#6738 · 4 comments ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
Textualize/textual#6735 · 1 comment ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Textualize/textual#6734 · 2 comments ·
All issues in Textualize/textual
Similar issues
-
Remove the `pull_request_target` workflow before GitHub blocks itPossibly taken @jat255 claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 2 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
IBM/mcp-context-forge#7483 ·
Maintainers usually reply within 2 days
-
bug good first issue
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
[Bug Report]: OpenAI 辅助 API 连通性测试因输出上限仅 1 token 误报“未知错误”Possibly taken @fjfanjian claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
Project-N-E-K-O/N.E.K.O#3640 ·
Maintainers usually reply within 1 day
-
author-page correction
Difficulty 2/5 1-3 hours Newbie friendliness 64/100
acl-org/acl-anthology#10808 · 1 comment ·
Maintainers usually reply within 1 day