langgenius/dify · error · AppUnavailableError
app_unavailable
app_unavailable
Error message
App unavailable, please check your app configurations.
What it means
AppUnavailableError (HTTP 400, code 'app_unavailable') is raised in ConversationListApi.get at /installed-apps/<id>/conversations when installed_app.app_with_session(session) returns None. The InstalledApp row exists but the underlying App record it references is gone — deleted by the owner, purged, or corrupted.
Source
Thrown at api/controllers/console/explore/conversation.py:59
console_ns,
ConversationInfiniteScrollPagination,
ResultResponse,
SimpleConversation,
)
@console_ns.route(
"/installed-apps/<uuid:installed_app_id>/conversations",
endpoint="installed_app_conversations",
)
class ConversationListApi(InstalledAppResource):
@console_ns.doc(params=query_params_from_model(ConversationListQuery))
@console_ns.response(200, "Success", console_ns.models[ConversationInfiniteScrollPagination.__name__])
@with_current_user
def get(self, current_user: Account, installed_app: InstalledApp):
app_model = installed_app.app_with_session(session=db.session())
if app_model is None:
raise AppUnavailableError()
app_mode = AppMode.value_of(app_model.mode)
if app_mode not in {AppMode.CHAT, AppMode.AGENT_CHAT, AppMode.ADVANCED_CHAT}:
raise NotChatAppError()
raw_args: dict[str, Any] = {
"last_id": request.args.get("last_id"),
"limit": request.args.get("limit", default=20, type=int),
"pinned": request.args.get("pinned"),
}
if raw_args["last_id"] is None:
raw_args["last_id"] = None
pinned_value = raw_args["pinned"]
if isinstance(pinned_value, str):
raw_args["pinned"] = pinned_value == "true"
args = ConversationListQuery.model_validate(raw_args)
try:
with sessionmaker(db.engine).begin() as session:View on GitHub (pinned to ef8544b173)
Solutions
- Uninstall the orphaned installed app via DELETE /console/explore/installed-apps/<installed_app_id>.
- Verify with the app owner whether the app still exists; reinstall from Explore if available.
- If this affects many users, check for a batch deletion or migration that removed App rows without cleaning up InstalledApp references.
Defensive patterns
Strategy: validation
Validate before calling
async function appExists(installedAppId) {
const res = await fetch(`/console/explore/installed-apps/${installedAppId}`);
return res.ok;
} Try / catch
try {
const conversations = await listConversations(installedAppId);
} catch (e) {
if (e.code === 'app_unavailable') {
// backing App gone — hide the conversation list, offer uninstall
showAppUnavailableUI(installedAppId);
}
} Prevention
- Verify the installed app's backing App exists before listing conversations.
- Handle app_unavailable by prompting the user to uninstall the orphaned installed app.
- Clean up stale installed apps whose source App was deleted.
When it happens
Trigger: GET /console/explore/installed-apps/<installed_app_id>/conversations where the original App was deleted by its owning tenant after installation, leaving an orphaned InstalledApp.
Common situations: App owner deleted the app from their workspace; tenant purge removed the App row; database inconsistency; the app is being decommissioned and the App was removed first.
Related errors
AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12).
Data as JSON: /api/errors/ae2cd4d58d218217.
Report an issue: GitHub.