langgenius/dify · error · Forbidden
Forbidden
Error message
Forbidden
What it means
Raised as werkzeug Forbidden() (HTTP 403) in PublishedAllRagPipelineApi.get (GET /rag/pipelines/<pipeline_id>/workflows). The list endpoint accepts a `user_id` filter, but a caller may only filter by their own account; any other user_id is rejected. This is an intentional authorization guard, not a bug.
Source
Thrown at api/controllers/console/datasets/rag_pipeline/rag_pipeline_workflow.py:668
@account_initialization_required
@edit_permission_required
@rbac_permission_required(RBACResourceScope.DATASET, RBACPermission.DATASET_EDIT)
@with_current_user
@get_rag_pipeline
@model_validate(WorkflowListQuery)
def get(self, req_data: WorkflowListQuery, current_user: Account, pipeline: Pipeline):
"""
Get published workflows
"""
page = req_data.page
limit = req_data.limit
user_id = req_data.user_id
named_only = req_data.named_only
if user_id:
if user_id != current_user.id:
raise Forbidden()
rag_pipeline_service = RagPipelineService(db.session())
with sessionmaker(db.engine).begin() as session:
workflows, has_more = rag_pipeline_service.get_all_published_workflow(
session=session,
pipeline=pipeline,
page=page,
limit=limit,
user_id=user_id,
named_only=named_only,
)
return WorkflowPaginationResponse.model_validate(
{
"items": workflows,
"page": page,
"limit": limit,
"has_more": has_more,View on GitHub (pinned to ef8544b173)
Solutions
- Omit user_id to list published workflows for the pipeline without a user filter.
- Only pass user_id equal to the currently authenticated account's id.
- If cross-user listing is a product requirement, implement it through an admin/owner-scoped route with proper RBAC rather than this endpoint.
Example fix
// before GET /rag/pipelines/<id>/workflows?user_id=<other-user> // after GET /rag/pipelines/<id>/workflows // no user_id filter // or GET /rag/pipelines/<id>/workflows?user_id=<self>
Defensive patterns
Strategy: validation
Validate before calling
function listWorkflows(pipelineId, selfUserId, opts = {}) {
const params = new URLSearchParams({ page: opts.page ?? 1, limit: opts.limit ?? 20 });
if (opts.userId) {
if (opts.userId !== selfUserId) throw new Error('can only filter by own user_id');
params.set('user_id', opts.userId);
}
if (opts.namedOnly) params.set('named_only', 'true');
return fetch(`/rag/pipelines/${pipelineId}/workflows?${params}`);
} Type guard
function isOwnUserFilter(userId, selfId) {
return userId == null || userId === selfId;
} Try / catch
try { await listWorkflows(id, self, { userId }); } catch (e) { if (/forbidden/i.test(e.message)) useNoUserFilter(); else throw e; } Prevention
- Default to omitting user_id.
- Never send another user's id from this client.
- Surface 403 as 'view your own workflows only'.
When it happens
Trigger: Calling GET /rag/pipelines/<pipeline_id>/workflows?user_id=<X> where <X> differs from the authenticated current_user.id. The check `if user_id != current_user.id` fires and raises Forbidden().
Common situations: A client hardcoding or caching another user's id; an admin tool assuming cross-user visibility that the endpoint does not grant; stale session token after account switch.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- usage_missing_arg
- usage_missing_arg
- usage_invalid_flag
- Workflow node execution not found
- Invalid filters
AI-assisted analysis of langgenius/dify@ef8544b173 (2026-08-12).
Data as JSON: /api/errors/4e774ef405cfe652.
Report an issue: GitHub.