Textualize/textual · error · ValueError
{variable_name} should contain a Screen type or callable, no
Error message
{variable_name} should contain a Screen type or callable, not an instance (got instance of {type(screen_object).__name__} for {screen_name!r}) What it means
Raised in App.__init_subclass__ when a SCREENS or MODES dict entry is an instantiated Screen object. Textual requires the class/callable (factory) so it can construct fresh screens, not a shared instance.
Source
Thrown at src/textual/app.py:920
@property
def _is_devtools_connected(self) -> bool:
"""Is the app connected to the devtools?"""
return self.devtools is not None and self.devtools.is_connected
@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_eventView on GitHub (pinned to 06dbeef4bb)
Solutions
- Store the Screen subclass itself: SCREENS = {"main": MainScreen}
- Or store a callable/string: SCREENS = {"main": "other_screen_name"} or a lambda returning a Screen
Example fix
# before
class MyApp(App):
SCREENS = {"main": MainScreen()}
# after
class MyApp(App):
SCREENS = {"main": MainScreen} Defensive patterns
Strategy: validation
Validate before calling
for name, s in SCREENS.items():
assert isinstance(s, str) or (callable(s) and not isinstance(s, Screen)), f'{name}: pass the class, not an instance' Type guard
def is_valid_screen_entry(v) -> bool:
return isinstance(v, str) or (callable(v) and not isinstance(getattr(v, '__self__', None), type) and not isinstance(v, Screen)) Prevention
- Store Screen subclasses in SCREENS/MODES
- Never instantiate screens at class-definition time
When it happens
Trigger: Writing SCREENS = {"main": MainScreen()} on an App subclass — the class-level validation on subclass creation detects the instance.
Common situations: Migrating code that stored pre-built screen instances; misunderstanding SCREENS as a registry of live objects rather than factories.
Related errors
- expected a callable or string, got {screen_object!r}
- MODES cannot contain instances, use a type instead (got inst
- add_mode() must be called with a Screen type, not an instanc
- 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/e30c1bc2c97b935b.
Report an issue: GitHub.