Textualize/textual · error · CallbackError
unable to run callback {event.callback!r}; {error}
Error message
unable to run callback {event.callback!r}; {error} What it means
Raised by Textual's message pump when a timer callback (e.g. set_interval / set_timeout callback) raises an unhandled exception. The timer fires, the callback is invoked, and any exception it throws is wrapped in CallbackError with the original error text appended.
Source
Thrown at src/textual/message_pump.py:918
await invoke(event.callback)
async def on_timer(self, event: events.Timer) -> None:
if not self.app._running:
return
event.prevent_default()
event.stop()
if event.callback is not None:
try:
self.app.screen
except Exception:
self.log.warning(
f"Not invoking timer callback {event.callback!r} because there is no screen."
)
return
try:
await invoke(event.callback)
except Exception as error:
raise CallbackError(
f"unable to run callback {event.callback!r}; {error}"
)
View on GitHub (pinned to 06dbeef4bb)
Solutions
- Fix the underlying exception shown after the semicolon in the message
- Wrap the callback body in try/except if failures are expected
- Guard against dead/removed widgets inside the callback before using them
Example fix
// before
def refresh(self) -> None:
self.query_one("#status").update(self.api.status())
// after
def refresh(self) -> None:
try:
self.query_one("#status").update(self.api.status())
except Exception:
self.log("refresh failed") Defensive patterns
Strategy: try-catch
Try / catch
def safe_tick(self) -> None:
try:
self._tick()
except Exception:
self.log.error("timer callback failed", exc_info=True) Prevention
- Keep timer callbacks short and defensive
- Log exceptions inside callbacks rather than letting them propagate
- Check widget.is_running before doing work in a tick
When it happens
Trigger: A callback passed to app.set_interval(), app.set_timeout(), or widget.set_interval() raises an exception (AttributeError, KeyError, network error, etc.) while the app is running.
Common situations: Timer callbacks that reference widgets that were removed, callbacks that hit external APIs which intermittently fail, or None dereferences in periodic refresh code.
Related errors
- Can not create a worker from a non-async function unless `th
- Timed out while waiting for widgets to process pending messa
- RLock.release called too many times
- Can't await screen.dismiss() from the screen's message handl
- Request to run a non-async function as an async worker
AI-assisted analysis of Textualize/textual@06dbeef4bb (2026-08-27).
Data as JSON: /api/errors/238deb2638612f7d.
Report an issue: GitHub.