langgenius/dify · error · AppUnavailableError
app_unavailable
app_unavailable
Error message
App unavailable, please check your app configurations.
What it means
Raised by CompletionApi.post (POST /console/installed-apps/<installed_app_id>/completion-messages) when installed_app.app_with_session() returns None, meaning the InstalledApp record exists but its referenced App model is missing from the database. Returns HTTP 400 with error_code 'app_unavailable'. The Explore 'installed app' layer is a pointer to a real App; if that App was deleted, archived, or became tenant-inaccessible after installation, the pointer dangles and this guard fires.
Source
Thrown at api/controllers/console/explore/completion.py:102
"/installed-apps/<uuid:installed_app_id>/completion-messages",
endpoint="installed_app_completion",
)
class CompletionApi(InstalledAppResource):
@console_ns.expect(console_ns.models[CompletionMessageExplorePayload.__name__])
@console_ns.response(200, "Success")
@with_current_user
@with_session
@model_validate(CompletionMessageExplorePayload)
def post(
self,
req_data: CompletionMessageExplorePayload,
session: Session,
current_user: Account,
installed_app: InstalledApp,
):
app_model = installed_app.app_with_session(session=session)
if app_model is None:
raise AppUnavailableError()
if app_model.mode != AppMode.COMPLETION:
raise NotCompletionAppError()
args = req_data.model_dump(exclude_none=True)
streaming = req_data.response_mode == "streaming"
args["auto_generate_name"] = False
installed_app.last_used_at = naive_utc_now()
db.session.commit()
try:
response = AppGenerateService.generate(
session=session,
app_model=app_model,
user=current_user,
args=args,
invoke_from=InvokeFrom.EXPLORE,View on GitHub (pinned to ef8544b173)
Solutions
- Reinstall the app from the Explore marketplace so a fresh InstalledApp row points at a live App.
- Confirm the installed_app_id in the URL is current and was not copied from a stale bookmark.
- Ask the workspace admin to verify the App still exists in Studio and is published.
- If you are the admin, audit InstalledApp rows for orphaned app_id values and remove them.
Example fix
// before: calling completion on a stale installed_app_id
await fetch(`/console/installed-apps/${staleInstalledAppId}/completion-messages`, {method:'POST', body: ...})
// after: re-fetch the installed app list first, then call with a known-good id
const installed = await fetch('/console/installed-apps').then(r => r.json())
const live = installed.data.find(i => i.app_id === expectedAppId)
await fetch(`/console/installed-apps/${live.id}/completion-messages`, {method:'POST', body: ...}) Defensive patterns
Strategy: validation
Validate before calling
// Before posting to completion-messages, confirm the installed app resolves to a live app.
async function getLiveAppId(installedAppId, headers) {
const res = await fetch(`/console/installed-apps`, { headers });
if (!res.ok) return null;
const list = await res.json();
const entry = (list.data ?? []).find(i => i.id === installedAppId);
if (!entry) return null;
// Explore deletes InstalledApp rows whose app is gone, so presence implies a live app.
return entry.id;
}
// usage
const liveId = await getLiveAppId(installedAppId, headers);
if (!liveId) { /* prompt reinstall instead of calling */ } Type guard
function isUsableInstalledApp(entry) {
return Boolean(entry && entry.id && entry.app_id && entry.tenant_id);
} Try / catch
try {
await postCompletion(installedAppId, payload);
} catch (err) {
if (err.code === 'app_unavailable') {
// reconcile: refetch installed apps, prompt reinstall, do not retry same id
await refreshInstalledApps();
} else { throw err; }
} Prevention
- Cache installed_app_id only for the session; re-fetch the installed-apps list on reload to drop stale ids.
- When an app owner deletes an app, propagate that to installed-app consumers via a refresh.
- Do not hardcode installed_app_id values in clients; always resolve from the API.
When it happens
Trigger: POST /console/installed-apps/<uuid:installed_app_id>/completion-messages where the InstalledApp's underlying App row is gone (deleted by owner, migrated away, or tenant mismatch). The installed_app_required decorator runs a best-effort cleanup check but uses a separate session, so a race or a session-cache miss lets the request reach this guard.
Common situations: App owner deleted the app from the Studio after other users installed it from Explore; workspace/tenant data migration left orphan InstalledApp rows; the app was duplicated/moved and the original ID no longer resolves; cross-tenant access where app_id belongs to a different tenant.
Related errors
- not_completion_app
- Conversation Not Exists.
- conversation_completed
- provider_not_initialize
- provider_quota_exceeded
AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12).
Data as JSON: /api/errors/2718ce88dca593ac.
Report an issue: GitHub.