{"record":{"id":"e30c1bc2c97b935b","repo":"Textualize/textual","slug":"variable-name-should-contain-a-screen-type-or-ca","errorCode":null,"errorMessage":"{variable_name} should contain a Screen type or callable, not an instance (got instance of {type(screen_object).__name__} for {screen_name!r})","messagePattern":"(.+?) should contain a Screen type or callable, not an instance \\(got instance of (.+?) for (.+?)\\)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/textual/app.py","lineNumber":920,"sourceCode":"    @property\n    def _is_devtools_connected(self) -> bool:\n        \"\"\"Is the app connected to the devtools?\"\"\"\n        return self.devtools is not None and self.devtools.is_connected\n\n    @cached_property\n    def _exception_event(self) -> asyncio.Event:\n        \"\"\"An event that will be set when the first exception is encountered.\"\"\"\n        return asyncio.Event()\n\n    def __init_subclass__(cls, *args, **kwargs) -> None:\n        for variable_name, screen_collection in (\n            (\"SCREENS\", cls.SCREENS),\n            (\"MODES\", cls.MODES),\n        ):\n            for screen_name, screen_object in screen_collection.items():\n                if not (isinstance(screen_object, str) or callable(screen_object)):\n                    if isinstance(screen_object, Screen):\n                        raise ValueError(\n                            f\"{variable_name} should contain a Screen type or callable, not an instance\"\n                            f\" (got instance of {type(screen_object).__name__} for {screen_name!r})\"\n                        )\n                    raise TypeError(\n                        f\"expected a callable or string, got {screen_object!r}\"\n                    )\n\n        return super().__init_subclass__(*args, **kwargs)\n\n    def _thread_init(self):\n        \"\"\"Initialize threading primitives for the current thread.\n\n        https://github.com/Textualize/textual/issues/5845\n\n        \"\"\"\n        self._message_queue\n        self._mounted_event\n        self._exception_event","sourceCodeStart":902,"sourceCodeEnd":938,"githubUrl":"https://github.com/Textualize/textual/blob/06dbeef4bb70fb718236aa418ed658ef4667a126/src/textual/app.py#L902-L938","documentation":"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.","triggerScenarios":"Writing SCREENS = {\"main\": MainScreen()} on an App subclass — the class-level validation on subclass creation detects the instance.","commonSituations":"Migrating code that stored pre-built screen instances; misunderstanding SCREENS as a registry of live objects rather than factories.","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"],"exampleFix":"# before\nclass MyApp(App):\n    SCREENS = {\"main\": MainScreen()}\n\n# after\nclass MyApp(App):\n    SCREENS = {\"main\": MainScreen}","handlingStrategy":"validation","validationCode":"for name, s in SCREENS.items():\n    assert isinstance(s, str) or (callable(s) and not isinstance(s, Screen)), f'{name}: pass the class, not an instance'","typeGuard":"def is_valid_screen_entry(v) -> bool:\n    return isinstance(v, str) or (callable(v) and not isinstance(getattr(v, '__self__', None), type) and not isinstance(v, Screen))","tryCatchPattern":null,"preventionTips":["Store Screen subclasses in SCREENS/MODES","Never instantiate screens at class-definition time"],"tags":["screens","modes","subclass-validation","textual"],"backgroundTag":"instance-instead-of-class","analyzedSha":"06dbeef4bb70fb718236aa418ed658ef4667a126","analyzedAt":"2026-08-27T02:36:57.214Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}