Skip to content

TUI can crash on stale SelectableStatic MouseDown after message refresh #460

@HamsteRider-m

Description

@HamsteRider-m

Problem

The Textual TUI can crash when a mouse selection event targets a message widget that has just been unmounted during a refresh/remount cycle.

The observed traceback ends in Textual's App.on_event path with an assertion that the mouse-down widget still has a parent. In this session the target was a SelectableStatic message body whose parent was already None.

Classification

This looks like an application/UI race bug, not a model failure, user misuse, or a local-only environment issue:

  • The failure happens before the agent/model logic handles the turn.
  • Textual reasonably assumes the mouse target is still mounted when dispatching the event.
  • GenericAgent's TUI dynamically removes/remounts message widgets while interaction events may still be in flight.
  • The local environment only exposes the race (textual 8.2.7, Python 3.14.5); it is not caused by a user command or invalid configuration.

Root cause

frontends/tuiapp_v2.py renders message bodies as SelectableStatic widgets. During message refresh/layout updates, existing message widgets may be removed and remounted. If a MouseDown/selection event is still queued for one of the removed widgets, Textual's event dispatch path can encounter a detached widget and assert because the target no longer has a parent.

Impact

A normal terminal interaction such as selecting/clicking text while the TUI is refreshing can terminate the whole TUI session.

Proposed fix

Make the TUI resilient to stale selection mouse events by ignoring events whose target widget is no longer attached to the widget tree before Textual reaches the parent assertion path. Add focused regression coverage for detached SelectableStatic/message widgets.

Acceptance criteria

  • Detached/stale SelectableStatic mouse events are ignored instead of crashing.
  • Normal selection/click behavior on mounted message widgets continues to work.
  • A regression test covers a detached message widget receiving a mouse event.
  • Existing TUI tests still pass.

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