Textualize/textual · error · TypeError
expected a callable or string, got {screen_object!r}
Error message
expected a callable or string, got {screen_object!r} What it means
TypeError raised in App.__init_subclass__ when a SCREENS/MODES entry is neither a string nor callable and also not a Screen instance (e.g. an int, None, or arbitrary object). Class-level validation runs as soon as the App subclass is defined.
Source
Thrown at src/textual/app.py:924
@cached_property
def _exception_event(self) -> asyncio.Event:
"""An event that will be set when the first exception is encountered."""
return asyncio.Event()
def __init_subclass__(cls, *args, **kwargs) -> None:
for variable_name, screen_collection in (
("SCREENS", cls.SCREENS),
("MODES", cls.MODES),
):
for screen_name, screen_object in screen_collection.items():
if not (isinstance(screen_object, str) or callable(screen_object)):
if isinstance(screen_object, Screen):
raise ValueError(
f"{variable_name} should contain a Screen type or callable, not an instance"
f" (got instance of {type(screen_object).__name__} for {screen_name!r})"
)
raise TypeError(
f"expected a callable or string, got {screen_object!r}"
)
return super().__init_subclass__(*args, **kwargs)
def _thread_init(self):
"""Initialize threading primitives for the current thread.
https://github.com/Textualize/textual/issues/5845
"""
self._message_queue
self._mounted_event
self._exception_event
self._thread_id = threading.get_ident()
def _get_dom_base(self) -> DOMNode:
"""When querying from the app, we want to query the default screen."""View on GitHub (pinned to 06dbeef4bb)
Solutions
- Replace the entry with a Screen subclass, a callable returning a Screen, or a screen name string
- Log/validate dict contents when building SCREENS dynamically before class creation
Example fix
# before
class MyApp(App):
SCREENS = {"main": None}
# after
class MyApp(App):
SCREENS = {"main": "main"} # or MainScreen class Defensive patterns
Strategy: type-guard
Validate before calling
assert all(isinstance(v, str) or callable(v) for v in [*SCREENS.values(), *MODES.values()])
Type guard
def valid_entry(v) -> bool:
return isinstance(v, str) or callable(v) Prevention
- Keep SCREENS/MODES values as classes, callables, or strings
- Unit-test App subclass imports to catch __init_subclass__ errors early
When it happens
Trigger: SCREENS = {"main": 42} or MODES = {"settings": None} in an App subclass definition.
Common situations: Typos, leftover placeholder values, or programmatically built dicts that inject non-screen values.
Related errors
- MODES cannot contain instances, use a type instead (got inst
- add_mode() must be called with a Screen type, not an instanc
- {variable_name} should contain a Screen type or callable, no
- No mode called {mode!r}
- No known mode {self._current_mode!r}
AI-assisted analysis of Textualize/textual@06dbeef4bb (2026-08-27).
Data as JSON: /api/errors/d0a5c78a45276692.
Report an issue: GitHub.