langgenius/dify · error · AppNotFoundError

app_not_found

app_not_found

Error message

App not found.

What it means

Raised by the app_data_view decorator when an app is bound to an Agent App with AgentScope.WORKFLOW_ONLY. Such hidden backing apps power a roster Agent but are not part of the general app management plane, so generic app routes reject them outright. Returned as code 'app_not_found' to avoid leaking the hidden backing app.

Source

Thrown at api/controllers/console/app/wraps.py:83

    (rename/icon sync, archive, API enablement), so it additionally requires
    workspace ``agent.manage`` on top of the route's existing App permission
    checks when RBAC is enabled. A no-op for non-agent Apps. Must be placed
    above ``get_app_model`` so the ``app_id`` path parameter is still present.
    """

    @wraps(view)
    def decorated(*args: P.args, **kwargs: P.kwargs) -> R:
        raw_app_id = kwargs.get("app_id") or kwargs.get("resource_id")
        if raw_app_id is not None:
            app_model = _load_app_model_from_scoped_session(str(raw_app_id))
            binding = (
                app_model.agent_app_binding_with_session(session=db.session(), include_archived=True)
                if app_model is not None
                else None
            )
            if binding is not None:
                if binding.scope == AgentScope.WORKFLOW_ONLY:
                    raise AppNotFoundError()
                if dify_config.RBAC_ENABLED:
                    current_user, current_tenant_id = current_account_with_tenant()
                    enforce_rbac_access(
                        tenant_id=current_tenant_id,
                        account_id=current_user.id,
                        resource_type=RBACResourceScope.WORKSPACE,
                        scene=RBACPermission.AGENT_MANAGE,
                        resource_required=False,
                    )
        return view(*args, **kwargs)

    return decorated


def _get_injected_session(args: tuple[object, ...]) -> Session | None:
    """Return the request session inserted by `with_session`, if this handler has been migrated."""
    if len(args) < 2:
        return None

View on GitHub (pinned to ef8544b173)

Solutions

  1. Use the agent App id (the one shown in the console), not the hidden workflow-only backing app id.
  2. Inspect AgentAppBinding to find the user-facing app id associated with the backing workflow.
  3. Filter UI lists to exclude apps with a WORKFLOW_ONLY binding.
Defensive patterns

Strategy: validation

Validate before calling

const app = await get(`/apps/${appId}`);
if (app.agent_app_binding?.scope === 'workflow_only') {
  // redirect to the user-facing agent app id instead
}

Try / catch

try {
  await get(`/apps/${appId}/...`);
} catch (e) {
  if (e.code === 'app_not_found' && isAgentBackingAppId(appId)) {
    // resolve and use the agent app id instead
  } else { throw e; }
}

Prevention

When it happens

Trigger: Any generic app-management route decorated with @app_data_view that receives the app_id of a hidden workflow-only backing app behind a roster Agent (e.g. hitting /apps/{app_id}/... on the backing App rather than the Agent App).

Common situations: Frontend mistakenly targets the backing workflow app id instead of the agent app id; URL copy/paste of the wrong id from the database; tooling that lists all App rows hits the hidden one.

Related errors


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