Textualize/textual · error · AppFail
Unable to find app in {lib!r}, try specifying app with "foo.
Error message
Unable to find app in {lib!r}, try specifying app with "foo.py:app" What it means
Textual's import_app() raises this when a file is loaded without an explicit app name and it contains no module-level `app` variable, no App instance, and no App subclass other than textual.app.App itself. Textual scans the file's globals for anything that is an App instance or an App subclass; an empty result means the file simply does not expose a Textual app at module scope. The fix suggested by the message itself is to use the `file:app` syntax.
Source
Thrown at src/textual/_import_app.py:97
if "app" in global_vars:
# App exists, lets use that
try:
app = global_vars["app"]
except KeyError:
raise AppFail(f"App {name!r} not found in {lib!r}")
else:
# Find an App class or instance that is *not* the base class
apps = [
value
for value in global_vars.values()
if (
isinstance(value, App)
or (inspect.isclass(value) and issubclass(value, App))
and value is not App
)
]
if not apps:
raise AppFail(
f'Unable to find app in {lib!r}, try specifying app with "foo.py:app"'
)
if len(apps) > 1:
raise AppFail(
f'Multiple apps found {lib!r}, try specifying app with "foo.py:app"'
)
app = apps[0]
app._BASE_PATH = path
else:
# Assuming the user wants to import the file
sys.path.append("")
try:
module = importlib.import_module(lib)
except ImportError as error:
raise AppFail(str(error))
find_app = name or "app"View on GitHub (pinned to 06dbeef4bb)
Solutions
- Specify the app explicitly with path:name syntax: `textual run foo.py:MyApp`
- Move the App subclass or instance to module level in the target file
- Point the command at the file that actually defines the app, not a helper module
- Import the app into the entry file so it becomes a global there
Example fix
# before
# myapp.py
def make_app():
class MyApp(App): ...
return MyApp()
# CLI: textual run myapp.py -> AppFail
# after
# myapp.py
class MyApp(App): ...
app = MyApp()
# CLI: textual run myapp.py:app Defensive patterns
Strategy: validation
Validate before calling
import runpy, inspect
from textual.app import App
def discover_apps(path: str) -> list:
g = runpy.run_path(path)
return [v for v in g.values()
if isinstance(v, App) or (inspect.isclass(v) and issubclass(v, App) and v is not App)]
# assert len(discover_apps("foo.py")) >= 1 before invoking textual Try / catch
try:
app = import_app("foo.py")
except AppFail as e:
# fall back to explicit naming
app = import_app("foo.py:MyApp") Prevention
- Give every entry file exactly one module-level App subclass or instance
- In tests, assert discover_apps(entry) is non-empty
- Avoid defining apps only inside functions or __main__ guards
When it happens
Trigger: Running `textual run foo.py` (or take_svg_screenshot on it) when foo.py only defines helper functions or imports App without subclassing it; the App subclass is nested inside another class or function; the file defines the app under `if __name__ == "__main__":` only; the file is a library module with no app.
Common situations: Pointing the CLI at the wrong file in a multi-file project; copy-pasted examples where the app class is defined in an imported module rather than the entry file; apps built inside factory functions like `def create_app(): return App()`; using an installed package name instead of a file path, so the scanned globals are the package's __init__.
Related errors
- App {name!r} not found in {lib!r}
- Multiple apps found {lib!r}, try specifying app with "foo.py
- Unable to find {find_app!r} in {module!r}
- SyntaxAwareDocument unavailable - tree-sitter is not install
- Can't animate attribute {attribute!r} on {obj!r}; attribute
AI-assisted analysis of Textualize/textual@06dbeef4bb (2026-08-27).
Data as JSON: /api/errors/7e04c02168e7b836.
Report an issue: GitHub.