{"record":{"id":"506ec1e7b1447bff","repo":"langgenius/dify","slug":"installed-app-not-found","errorCode":null,"errorMessage":"Installed app not found","messagePattern":"Installed app not found","errorType":"http","errorClass":"NotFound","httpStatus":404,"severity":"error","filePath":"api/controllers/console/explore/installed_app.py","lineNumber":256,"sourceCode":"@console_ns.route(\"/installed-apps/<uuid:installed_app_id>\")\nclass InstalledAppApi(InstalledAppResource):\n    \"\"\"\n    get, update, and delete an installed app\n    use InstalledAppResource to apply default decorators and get installed_app\n    \"\"\"\n\n    @console_ns.response(200, \"Success\", console_ns.models[InstalledAppResponse.__name__])\n    @with_current_user\n    @with_current_tenant_id\n    def get(\n        self,\n        current_tenant_id: str,\n        current_user: Account,\n        installed_app: InstalledApp,\n    ):\n        app_model = InstalledAppService.get_published_app(installed_app.app_id, session=db.session)\n        if app_model is None:\n            raise NotFound(\"Installed app not found\")\n        if current_user.current_tenant is None:\n            raise ValueError(\"current_user.current_tenant must not be None\")\n\n        current_user.role = TenantService.get_user_role(current_user, current_user.current_tenant, session=db.session())\n        return dump_response(\n            InstalledAppResponse,\n            _installed_app_response_data(\n                installed_app,\n                app_model,\n                current_tenant_id=current_tenant_id,\n                current_user=current_user,\n            ),\n        )\n\n    @console_ns.response(204, \"App uninstalled successfully\")\n    @with_current_tenant_id\n    def delete(self, current_tenant_id: str, installed_app: InstalledApp):\n        if installed_app.app_owner_tenant_id == current_tenant_id:","sourceCodeStart":238,"sourceCodeEnd":274,"githubUrl":"https://github.com/langgenius/dify/blob/ef8544b173fd6cd7a8e71df2cab576e52bebbfbc/api/controllers/console/explore/installed_app.py#L238-L274","documentation":"HTTP 404 raised by GET /console/explore/installed-apps/<installed_app_id>. The InstalledApp row itself loaded fine (via the InstalledAppResource decorator), but InstalledAppService.get_published_app returned None — i.e. no published App is reachable for that installed app.","triggerScenarios":"GET /console/explore/installed-apps/<id> where the underlying App was unpublished or deleted by its owner tenant after this tenant installed it. The installed_apps row remains but the published App backing it is gone.","commonSituations":"App owner deleted or unpublished the app; the App row exists but is no longer in a published state; cross-tenant teardown of the owning workspace left dangling installed_apps rows.","solutions":["On the client, treat this as 'app no longer available' and remove/hide the installed-app entry from the user's view.","Have the owning tenant re-publish or restore the App, then retry.","As admin, clean up installed_apps rows whose published App is missing: SELECT ia.* FROM installed_apps ia LEFT JOIN apps a ON a.id = ia.app_id WHERE a.id IS NULL OR <not-published-condition>.","Provide a 'reinstall from Explore' affordance so the user can re-add a working copy if the app is relisted."],"exampleFix":"// before: blindly open installed app detail\nconst detail = await get(`/console/explore/installed-apps/${id}`);\n\n// after: handle 404 by retiring the stale tile\ntry {\n  const detail = await get(`/console/explore/installed-apps/${id}`);\n} catch (e) {\n  if (e.status === 404) removeFromWorkspace(id);\n  else throw e;\n}","handlingStrategy":"try-catch","validationCode":"// Preflight the installed app before opening detail\nasync function publishedAppAvailable(installedAppId) {\n  const r = await fetch(`/console/explore/installed-apps/${installedAppId}`);\n  if (r.status === 404) return false;\n  if (!r.ok) throw r;\n  return true;\n}","typeGuard":null,"tryCatchPattern":"try {\n  return await get(`/console/explore/installed-apps/${id}`);\n} catch (e) {\n  if (e.status === 404 && /Installed app not found/.test(e.message)) {\n    removeInstalledAppTile(id); // backing App no longer published\n    return null;\n  }\n  throw e;\n}","preventionTips":["Treat backing-app 404 as terminal for that tile; do not retry indefinitely.","Admins: clean up installed_apps when an App is unpublished or deleted.","Show a graceful 'app no longer available' state in the dashboard."],"tags":["rest-api","explore","not-found","installed-app","dify"],"backgroundTag":null,"analyzedSha":"ef8544b173fd6cd7a8e71df2cab576e52bebbfbc","analyzedAt":"2026-08-12T05:15:17.394Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}