Skip to content

fix(tui): avoid NoMatches exception when SelectionList is missing on key events. - #4780

Open
hrshtmk wants to merge 5 commits into
archlinux:masterfrom
hrshtmk:fix/select-list-crash
Open

hrshtmk wants to merge 5 commits into
archlinux:masterfrom
hrshtmk:fix/select-list-crash

Conversation

@hrshtmk

@hrshtmk hrshtmk commented Sep 20, 2026 •

Copy link
Copy Markdown

Problem :

If a keystroke arrives during SelectionList transition or screen dismissal, an unhandled NoMatches exception is thrown, causing archinstall to crash:

NoMatches: No nodes match 'SelectionList' on SelectListScreen()

asciicast

Replication :

  1. Open the Additional packages menu.
  2. Type as quick as you can and smash/press backspace when the menu is transitioning.

Reasons for the problem I figured :

  1. Key events can throw NoMatches exception if SelectionList is not visible in DOM. (First assumption)
  2. While fixing the list UI issues in Additional packages I figured that the main reason for the crash could have been the _update_options(...) being called over every single keystroke, which did the entire search cycle of filtering --> sorting --> widget on every key press by calling _group.get_focused_index(...).
    Which is expensive on larger lists and due to keystrokes overlapping the cycle queries the system crashed.

note : This issue was already marked in the menu_item.py but I think was never guarded against.

	def get_focused_index(self) -> int | None:
		items = self.get_enabled_items()

		if self.focus_item and items:
			try:
				return items.index(self.focus_item)
			except ValueError:
				# on large menus (15k+) when filtering very quickly
				# the index search is too slow while the items are reduced
				# by the filter and it will blow up as it cannot find the
				# focus item
				pass

		return None

Fix :

  1. Added 2 helper functions to guard query_one calls on key events.
	def _selection_list(self) -> SelectionList | None: ...
	def _preview_widget(self) -> Label | None: ...
  1. Made so that only a batch of keystrokes can call the _update_options by adding a slight delay (60ms, was found to be best in testing) on the event in on_input_changed. The delay is minuscule and doesn't really affect the overall UI experience.

@hrshtmk
hrshtmk requested a review from Torxed as a code owner September 20, 2026 18:35
@svartkanin

Copy link
Copy Markdown
Collaborator

it would be better to actual raise an issue and then link a PR with the fix to the issue. Folks who come search for similar problems will most likely search in the issues rather than past PRs.

@svartkanin

Copy link
Copy Markdown
Collaborator

Based on the description I assume this is LLM generated.
I cannot reproduce this so as long as you can't provide steps to reproduce it I can't validate the fix works.

@hrshtmk

hrshtmk commented Sep 26, 2026

Copy link
Copy Markdown
Author

Hi @svartkanin! Sorry for the LLM jump scare 😄
This is my first PR so I was trying to be very formal with the report.
I am linking the asciinema terminal recording below showing the exact replication steps.
asciicast

Now on replication, the best way is to :

  1. Open the Additional packages menu.
  2. Type as quick as you can and smash/press backspace when the menu is transitioning.
  3. Additionally, if you type a string that is gibberish (0 matches) and then crash the menu you should be able to replicate the bug on line 574.

Once you get the hang of it, it's easy to replicate.

2 key things I have noticed :

  1. The error doesn't log in the install.log of debug mode, for some reason. (Or I just wasn't able to catch it)
  2. The new bug that I encountered in this recording, on line 574, it tells us that this is probably an architectural oversight and it's basically a family of DOM bugs.

The reason for the bugs I think is due to the event handler always assuming that the SelectionList is present in the DOM whenever an event fires, so it just throws the NoMatches when the menu is transitioning or if there is a fast input.

To fix that my line 577 changes were basically checking if the list is present or not before performing events.
But now that we have encountered another bug on the similar front, I think the changes should be made in SelectionList or directly how the event handler works. I'd like to work on that too if you want!

@svartkanin

Copy link
Copy Markdown
Collaborator

Thanks for the explanation I was able to replicate it and confirm the fix

@svartkanin

Copy link
Copy Markdown
Collaborator

The mypy check is failing

@hrshtmk

This comment was marked as outdated.

@hrshtmk

hrshtmk commented Sep 27, 2026

Copy link
Copy Markdown
Author

I've kept on digging on this issue, trying to fix those "Issues that can be worked on next:" I mentioned in the earlier comment. My intuition was that the zero match freeze was somehow related to the NoMatches handling, and I was somewhat correct. This is not strictly a DOM related bug, but SelectListScreen._update_options() runs complete sorting and rendering of the filtered list on every single keystroke. With smaller lists this has no issues, but when the list is big, stacking these operations on top of each other on every keystroke was leaving the SelectionList unresponsive rather than crashing outright.

To fix this issue I added a slight delay between each of the operations and also refactored the code to fire the operations only on batches of keystrokes. The delay is minimal, at 60ms (best performance I found while testing), and can be tuned later if needed.
Also, I have kept the guards over every query_one call but created helper functions to reduce footprint.
I think the issue is fixed in the scope of the bug, happy to work further if you spot something I missed.

@hrshtmk
hrshtmk requested a review from svartkanin September 27, 2026 14:59

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants