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

  1. Omit user_id to list published workflows for the pipeline without a user filter.
  2. Only pass user_id equal to the currently authenticated account's id.
  3. 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

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

Related errors


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