Textualize/textual · warning · NoHandler

No handler for {event_name!r}

Error message

No handler for {event_name!r}

What it means

extract_handler_actions inspects a method's metadata (keys starting with '@' registered by the @on decorator) to find a handler matching an event path like ('Notification', 'Show'). If no metadata entry matches the event being brokered, it raises NoHandler. Internally this is often caught by _broker_event to skip the method, but when called directly it signals the method has no @on decorator for that event.

Source

Thrown at src/textual/_event_broker.py:37

    Args:
        event_name: Event to check from.
        meta: Meta information (stored in Rich Style)

    Raises:
        NoHandler: If no handler is found.

    Returns:
        Action information.
    """
    event_path = event_name.split(".")
    for key, value in meta.items():
        if key.startswith("@"):
            name_args = key[1:].split(".")
            if name_args[: len(event_path)] == event_path:
                modifiers = name_args[len(event_path) :]
                return HandlerArguments(set(modifiers), value)
    raise NoHandler(f"No handler for {event_name!r}")

View on GitHub (pinned to 06dbeef4bb)

Solutions

  1. Decorate the target method with @on matching the event class, e.g. @on(NotificationShow)
  2. Verify the event_path passed matches the decorated event, including any modifiers after the '.'
  3. Use widget.post_message/standard dispatch instead of calling extract_handler_actions directly
  4. Catch NoHandler when iterating candidate methods, mirroring _broker_event

Example fix

# before
def handle(self, event) -> None: ...
extract_handler_actions(handle, ('Notification', 'Show'))  # NoHandler

# after
from textual import on

@on(NotificationShow)
def handle(self, event) -> None: ...
extract_handler_actions(handle, ('Notification', 'Show'))
Defensive patterns

Strategy: try-catch

Validate before calling

from textual import on

def has_handler_for(method, event_cls) -> bool:
    for meta_key in getattr(method, '__textual_on', {}) or {}:
        if meta_key.lstrip('@').split('.')[:1] == [event_cls.__name__]:
            return True
    return False

Try / catch

from textual._event_broker import NoHandler
try:
    args = extract_handler_actions(method, event_path)
except NoHandler:
    args = None  # skip this method, not a handler for the event

Prevention

When it happens

Trigger: Calling extract_handler_actions(method, event_path) on a plain method or a method decorated with @on for a different event; e.g. extracting handlers for ('Notification','Show') from a method decorated @on(Button.Pressed).

Common situations: Direct use of the private _event_broker API in tests or custom dispatch loops; decorators missing or applied to the wrong event after refactoring; stale metadata after editing @on arguments.

Related errors


AI-assisted analysis of Textualize/textual@06dbeef4bb (2026-08-27). Data as JSON: /api/errors/0451ec3860fcf791. Report an issue: GitHub.