n8n - 💡(How to fix) Fix Bug: Project Editor role gets 403 on /rest/users (works for Project Admin) — sidebar and Workflow History broken [2 comments, 3 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
n8n-io/n8n#29561Fetched 2026-05-01 05:52:31
View on GitHub
Comments
2
Participants
3
Timeline
10
Reactions
0
Author
Timeline (top)
commented ×2labeled ×2mentioned ×2subscribed ×2

Error Message

GET https://<host>/rest/users?take=2&skip=0&filter=%7B%22isPending%22%3Afalse%7D 403 (Forbidden)

ResponseError: Listing all users is limited to instance administrators and project admins. Filter by project to list project members. at async Proxy.ie (users.store.ts:333:21) at async ProjectNavigation.vue:140:2

Code Example

GET https://<host>/rest/users?take=2&skip=0&filter=%7B%22isPending%22%3Afalse%7D 403 (Forbidden)

ResponseError: Listing all users is limited to instance administrators and project admins.
              Filter by project to list project members.
    at async Proxy.ie (users.store.ts:333:21)
    at async ProjectNavigation.vue:140:2

---

if (listQueryOptions.filter?.projectId) {
  // ok if user has project:list on that project
} else if (!['global:owner', 'global:admin'].includes(req.user.role.slug)) {
  const isProjectAdmin =
    (await this.projectService.getProjectIdsWithScope(req.user, ['project:update'])).length > 0;
  if (!isProjectAdmin) {
    throw new ForbiddenError(
      'Listing all users is limited to instance administrators and project admins. ...',
    );
  }
}

---

onBeforeMount(async () => {
     await usersStore.fetchUsers({ filter: { isPending: false }, take: 2 });
     ...
   });

---

const userIdsToFetch = new Set<string>(userIds);
   await usersStore.fetchUsers({ filter: { ids: Array.from(userIdsToFetch) } });
RAW_BUFFERClick to expand / collapse

Bug Description

A user whose only role is Project Editor gets a 403 from GET /rest/users whenever the project sidebar mounts, and again when opening Workflow History. The same flow works fine for Project Admin users — only Project Editors are blocked.

The UI shows the buttons and the history page as if available, but the network call fails and downstream rendering breaks.

Observed in the browser (signed in as a Project Editor):

GET https://<host>/rest/users?take=2&skip=0&filter=%7B%22isPending%22%3Afalse%7D 403 (Forbidden)

ResponseError: Listing all users is limited to instance administrators and project admins.
              Filter by project to list project members.
    at async Proxy.ie (users.store.ts:333:21)
    at async ProjectNavigation.vue:140:2

The same calls succeed for Project Admin and global admin/owner.

Why Project Admin works but Project Editor doesn't

packages/cli/src/controllers/users.controller.ts:121-138 (listUsers) only relaxes the admin requirement when the request is filtered by projectId. Otherwise it requires the caller to be a global admin/owner or a project admin (i.e. holds project:update on at least one project):

if (listQueryOptions.filter?.projectId) {
  // ok if user has project:list on that project
} else if (!['global:owner', 'global:admin'].includes(req.user.role.slug)) {
  const isProjectAdmin =
    (await this.projectService.getProjectIdsWithScope(req.user, ['project:update'])).length > 0;
  if (!isProjectAdmin) {
    throw new ForbiddenError(
      'Listing all users is limited to instance administrators and project admins. ...',
    );
  }
}
  • Project Admin has project:updateisProjectAdmin is true → request passes.
  • Project Editor does not have project:updateisProjectAdmin is false → 403, even though the project editor holds the global user:list scope (granted to every member by default) and the frontend therefore lets the call through.

The frontend users:list global scope and the backend's additional admin gate are out of sync — the frontend assumes the scope is sufficient, the backend only honors the scope for admins/project admins or when the request is scoped by projectId.

Affected callers

Two frontend callers fetch users without filtering by projectId, which is the only branch the backend allows for non-admins:

  1. Sidebar (every page mount)packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectNavigation.vue:140:

    onBeforeMount(async () => {
      await usersStore.fetchUsers({ filter: { isPending: false }, take: 2 });
      ...
    });
  2. Workflow History viewpackages/frontend/editor-ui/src/features/workflows/workflowHistory/views/WorkflowHistory.vue:165:

    const userIdsToFetch = new Set<string>(userIds);
    await usersStore.fetchUsers({ filter: { ids: Array.from(userIdsToFetch) } });

Both calls are bounded (take=2 in the sidebar, a closed set of user IDs in Workflow History), but neither is projectId-filtered, so both 403 for Project Editors.

#25024 ("chore(editor): Remove all calls to fetch all users and replace with targeted filtering") is in 2.18.4 and narrowed the calls, but did not relax the controller for these targeted filters, so Project Editors still hit 403.

Impact

  • Every page load with a Project Editor role logs a 403 in the console (sidebar mount).
  • Workflow History is effectively broken for Project Editors: the history list loads (via a different endpoint), but resolving the publishing user names triggers the 403, and the failure blocks the rest of the view from rendering as expected.
  • Buttons and nav items are displayed as if the feature is available, since the frontend permission check passes.
  • Project Admins are unaffected — the regression is specific to the Project Editor role.

Expected behavior

A Project Editor should be able to:

  • load any page without console 403s,
  • open Workflow History and see the names of users who published prior versions, without the call failing.

Possible directions:

  1. Backend: in users.controller.ts:listUsers, allow non-admin callers (including Project Editors) when the request is filtered by filter.ids (return only users matching the supplied id set, intersected with whatever the caller is allowed to see), and accept benign filters like { isPending: false } with a strict take limit. This matches how the calls are already shaped.
  2. Frontend: always pass a projectId (the current project context) for the sidebar call, and switch Workflow History to a project-scoped "resolve these user ids within this project" endpoint.

To Reproduce

  1. Enterprise instance with project roles enabled.
  2. Create a project, add a workflow with at least one published version, share the project with another user as Project Editor (specifically Editor — Project Admin works fine).
  3. Sign in as that Project Editor.
  4. Observe in DevTools → Network: every page load issues GET /rest/users?take=2&skip=0&filter={\"isPending\":false} → 403.
  5. Open the workflow → "Version History" → request to /rest/users?filter={\"ids\":[...]} → 403, history view fails to render fully.
  6. Repeat with the same user promoted to Project Admin → both calls succeed, the issue disappears.

Debug Info

  • n8n version: 2.18.4
  • Affected files:
    • packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectNavigation.vue (line 140)
    • packages/frontend/editor-ui/src/features/workflows/workflowHistory/views/WorkflowHistory.vue (line 165)
    • packages/cli/src/controllers/users.controller.ts (listUsers, lines ~121–138)
    • packages/frontend/editor-ui/src/features/settings/users/users.store.ts (fetchUsers, line ~324)

Environment

  • Operating System: Linux (self-hosted on GKE)
  • n8n Version: 2.18.4
  • Node.js Version: 24.x
  • Database: PostgreSQL
  • Execution mode: queue
  • Hosting: self hosted (Enterprise)

extent analysis

TL;DR

To fix the issue, modify the listUsers function in users.controller.ts to allow non-admin callers, including Project Editors, when the request is filtered by filter.ids or has a strict take limit.

Guidance

  • Identify the listUsers function in users.controller.ts and update the conditional statement to include Project Editors when the request is filtered by filter.ids or has a strict take limit.
  • Consider adding a check for the filter.ids parameter to ensure it only returns users matching the supplied id set, intersected with whatever the caller is allowed to see.
  • Review the frontend code to ensure it passes a projectId for the sidebar call and consider switching Workflow History to a project-scoped "resolve these user ids within this project" endpoint.
  • Test the changes with a Project Editor role to verify the fix.

Example

if (listQueryOptions.filter?.projectId) {
  // ok if user has project:list on that project
} else if (listQueryOptions.filter?.ids || (listQueryOptions.take && listQueryOptions.take <= 2)) {
  // allow non-admin callers, including Project Editors
} else if (!['global:owner', 'global:admin'].includes(req.user.role.slug)) {
  const isProjectAdmin =
    (await this.projectService.getProjectIdsWithScope(req.user, ['project:update'])).length > 0;
  if (!isProjectAdmin) {
    throw new ForbiddenError(
      'Listing all users is limited to instance administrators and project admins. ...',
    );
  }
}

Notes

The proposed fix assumes that the filter.ids parameter is used to fetch specific users and the take limit is used to prevent excessive data retrieval. Additional testing and validation may be necessary to ensure the fix works as expected.

Recommendation

Apply the workaround by modifying the listUsers function in

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

A Project Editor should be able to:

  • load any page without console 403s,
  • open Workflow History and see the names of users who published prior versions, without the call failing.

Possible directions:

  1. Backend: in users.controller.ts:listUsers, allow non-admin callers (including Project Editors) when the request is filtered by filter.ids (return only users matching the supplied id set, intersected with whatever the caller is allowed to see), and accept benign filters like { isPending: false } with a strict take limit. This matches how the calls are already shaped.
  2. Frontend: always pass a projectId (the current project context) for the sidebar call, and switch Workflow History to a project-scoped "resolve these user ids within this project" endpoint.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING