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

  1. Reinstall the app from the Explore marketplace so a fresh InstalledApp row points at a live App.
  2. Confirm the installed_app_id in the URL is current and was not copied from a stale bookmark.
  3. Ask the workspace admin to verify the App still exists in Studio and is published.
  4. 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

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


AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12). Data as JSON: /api/errors/2718ce88dca593ac. Report an issue: GitHub.