{"record":{"id":"d0a5c78a45276692","repo":"Textualize/textual","slug":"expected-a-callable-or-string-got-screen-object","errorCode":null,"errorMessage":"expected a callable or string, got {screen_object!r}","messagePattern":"expected a callable or string, got (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"src/textual/app.py","lineNumber":924,"sourceCode":"\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\n        self._thread_id = threading.get_ident()\n\n    def _get_dom_base(self) -> DOMNode:\n        \"\"\"When querying from the app, we want to query the default screen.\"\"\"","sourceCodeStart":906,"sourceCodeEnd":942,"githubUrl":"https://github.com/Textualize/textual/blob/06dbeef4bb70fb718236aa418ed658ef4667a126/src/textual/app.py#L906-L942","documentation":"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.","triggerScenarios":"SCREENS = {\"main\": 42} or MODES = {\"settings\": None} in an App subclass definition.","commonSituations":"Typos, leftover placeholder values, or programmatically built dicts that inject non-screen values.","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"],"exampleFix":"# before\nclass MyApp(App):\n    SCREENS = {\"main\": None}\n\n# after\nclass MyApp(App):\n    SCREENS = {\"main\": \"main\"}  # or MainScreen class","handlingStrategy":"type-guard","validationCode":"assert all(isinstance(v, str) or callable(v) for v in [*SCREENS.values(), *MODES.values()])","typeGuard":"def valid_entry(v) -> bool:\n    return isinstance(v, str) or callable(v)","tryCatchPattern":null,"preventionTips":["Keep SCREENS/MODES values as classes, callables, or strings","Unit-test App subclass imports to catch __init_subclass__ errors early"],"tags":["screens","modes","typeerror","textual"],"backgroundTag":"invalid-config-type","analyzedSha":"06dbeef4bb70fb718236aa418ed658ef4667a126","analyzedAt":"2026-08-27T02:36:57.214Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}