langgenius/dify · error · Forbidden
You can't install a non-public app
Error message
You can't install a non-public app
What it means
HTTP 403 Forbidden raised in POST /console/explore/installed-apps when the App row exists but app.is_public is False. The platform enforces that only apps explicitly marked public can be installed by other tenants through Explore.
Source
Thrown at api/controllers/console/explore/installed_app.py:213
@cloud_edition_billing_resource_check("apps")
@console_ns.expect(console_ns.models[InstalledAppCreatePayload.__name__])
@console_ns.response(200, "Success", console_ns.models[SimpleMessageResponse.__name__])
@with_current_tenant_id
@model_validate(InstalledAppCreatePayload)
def post(self, req_data: InstalledAppCreatePayload, current_tenant_id: str):
recommended_app = db.session.scalar(
select(RecommendedApp).where(RecommendedApp.app_id == req_data.app_id).limit(1)
)
if recommended_app is None:
raise NotFound("Recommended app not found")
app = db.session.get(App, req_data.app_id)
if app is None:
raise NotFound("App entity not found")
if not app.is_public:
raise Forbidden("You can't install a non-public app")
installed_app = db.session.scalar(
select(InstalledApp)
.where(and_(InstalledApp.app_id == req_data.app_id, InstalledApp.tenant_id == current_tenant_id))
.limit(1)
)
if installed_app is None:
# todo: position
recommended_app.install_count += 1
new_installed_app = InstalledApp(
app_id=req_data.app_id,
tenant_id=current_tenant_id,
app_owner_tenant_id=app.tenant_id,
is_pinned=False,
last_used_at=naive_utc_now(),
)View on GitHub (pinned to ef8544b173)
Solutions
- Have the app owner re-enable the public/published state on the app (Settings > Site > enable public access).
- Refresh the Explore listing on the client and only install apps that are still listed as public.
- If you need a private app, use the workspace invite/tenant flow instead of the Explore install endpoint.
- Verify no other tenant-admin toggled is_public off while you were installing.
Example fix
# before: app is private, install attempt fails
# owner action: in app Settings -> Site, turn on public access
# after: confirm flag before calling install
app = db.session.get(App, app_id)
if not app.is_public:
raise Forbidden("You can't install a non-public app")
# owner must publish first; client should hide the install button Defensive patterns
Strategy: validation
Validate before calling
// Only show Install for apps the listing marks as public
const listing = await getExploreListing();
const installable = listing.data.filter(a => a.is_public !== false);
if (!installable.some(a => a.app_id === targetId)) {
throw new UserError('App is not public; ask the owner to publish it');
} Type guard
function isPublicApp(app) {
return Boolean(app && app.is_public === true);
} Try / catch
try {
await post('/console/explore/installed-apps', { app_id });
} catch (e) {
if (e.status === 403 && /non-public app/.test(e.message)) {
notify('The owner must make this app public before it can be installed.');
} else throw e;
} Prevention
- Read is_public from the Explore listing and hide Install when false.
- Owners: keep is_public in sync with RecommendedApp entries.
- When an app is deliberately made private, expect clients to receive 403.
When it happens
Trigger: POST /console/explore/installed-apps with an app_id whose apps.is_public column is False, even though a RecommendedApp row exists. Common right after the app owner toggles the app to private, or when the app was listed but its public flag was never set.
Common situations: App owner disabled public access after publishing to Explore; app is in draft/private mode; tenant settings changed; client is using an app id obtained out-of-band (not from the public Explore feed).
Related errors
- You can't uninstall an app owned by the current tenant
- Recommended app not found
- Installed app not found
- app_more_like_this_disabled
- App entity not found
AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12).
Data as JSON: /api/errors/03ba3cd8fdadfe12.
Report an issue: GitHub.