{"record":{"id":"b4919d9cf6a2982b","repo":"Textualize/textual","slug":"unable-to-find-find-app-r-in-module-r","errorCode":null,"errorMessage":"Unable to find {find_app!r} in {module!r}","messagePattern":"Unable to find (.+?) in (.+?)","errorType":"exception","errorClass":"AppFail","httpStatus":null,"severity":"error","filePath":"src/textual/_import_app.py","lineNumber":119,"sourceCode":"                    raise AppFail(\n                        f'Multiple apps found {lib!r}, try specifying app with \"foo.py:app\"'\n                    )\n                app = apps[0]\n        app._BASE_PATH = path\n\n    else:\n        # Assuming the user wants to import the file\n        sys.path.append(\"\")\n        try:\n            module = importlib.import_module(lib)\n        except ImportError as error:\n            raise AppFail(str(error))\n\n        find_app = name or \"app\"\n        try:\n            app = getattr(module, find_app or \"app\")\n        except AttributeError:\n            raise AppFail(f\"Unable to find {find_app!r} in {module!r}\")\n\n        sys.argv[:] = [import_name, *argv]\n\n    if inspect.isclass(app) and issubclass(app, App):\n        app = app()\n\n    return cast(App, app)\n","sourceCodeStart":101,"sourceCodeEnd":127,"githubUrl":"https://github.com/Textualize/textual/blob/06dbeef4bb70fb718236aa418ed658ef4667a126/src/textual/_import_app.py#L101-L127","documentation":"Raised when Textual imports an app from an installed module (a dotted import path like `mylib.myapp`) and the attribute lookup for the requested name (or the default \"app\") fails with AttributeError. Unlike the file-based path, this branch uses importlib and getattr on the module object, so the name must be a real attribute of that package/module. It is wrapped in AppFail and typically surfaces from `textual run mylib.myapp:Something`.","triggerScenarios":"Running `textual run mylib:MyApp` where MyApp is not exported from mylib/__init__.py; using the default `app` name when the package exposes none; typo in the attribute or package path; the app lives in a submodule like mylib.app but you referenced mylib.","commonSituations":"Packages that don't re-export the app in __init__.py; renaming the public app variable without updating docs/CI commands; mixing up file paths and module paths (using dots where a path is needed); stale installed version of the package missing the symbol.","solutions":["Reference the exact submodule and attribute: `textual run mylib.app:app`","Re-export the app in the package's __init__.py (e.g. `from mylib.app import app`","Check for typos and the installed package version (pip show / pip install -e .)","If you meant a file on disk, use the path form `./mylib/app.py:app` instead of the dotted module form"],"exampleFix":"# before\n# mylib/__init__.py is empty; app lives in mylib/main.py\n# CLI: textual run mylib:app  -> AppFail\n\n# after\n# mylib/__init__.py\nfrom mylib.main import app\n# CLI: textual run mylib:app","handlingStrategy":"validation","validationCode":"import importlib\n\ndef module_has(module_name: str, attr: str) -> bool:\n    try:\n        mod = importlib.import_module(module_name)\n    except ImportError:\n        return False\n    return hasattr(mod, attr)\n\n# assert module_has(\"mylib\", \"app\") before textual run mylib:app","typeGuard":"def exposes_app(mod) -> TypeGuard[object]:\n    return hasattr(mod, \"app\")","tryCatchPattern":"try:\n    app = import_app(\"mylib:app\")\nexcept AppFail as e:\n    print(f\"Missing export: {e}\"); raise","preventionTips":["Re-export the app in your package __init__.py","Pin and verify the installed package version in CI","Double-check dotted module vs file-path syntax"],"tags":["textual","import","packaging","attribute-error"],"backgroundTag":"attribute-missing-in-package-export","analyzedSha":"06dbeef4bb70fb718236aa418ed658ef4667a126","analyzedAt":"2026-08-27T02:36:57.214Z","schemaVersion":2},"datasetVersion":"2026-08-27T03:17:27.898Z"}