{"record":{"id":"9e5f2572a3e48a11","repo":"different-ai/openwork","slug":"only-workspace-owners-and-super-admins-can-manage-9e5f25","errorCode":null,"errorMessage":"Only workspace owners and super-admins can manage roles.","messagePattern":"Only workspace owners and super-admins can manage roles\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ee/apps/den-web/app/(den)/dashboard/_providers/org-dashboard-provider.tsx","lineNumber":744,"sourceCode":"    }\n\n    await runMutation(\"transfer-ownership\", async () => {\n      ensureActiveOrganizationSelected();\n      const { response, payload } = await requestJson(\n        `/v1/members/${encodeURIComponent(memberId)}/transfer-ownership`,\n        { method: \"POST\", body: JSON.stringify({}) },\n        12000,\n      );\n\n      if (!response.ok) {\n        throw getRequestError(payload, response, `Failed to transfer ownership (${response.status}).`);\n      }\n    });\n  }\n\n  async function createRole(input: { roleName: string; permission: Record<string, string[]> }) {\n    if (!getCurrentAccess().canManageRoles) {\n      throw new Error(\"Only workspace owners and super-admins can manage roles.\");\n    }\n    ensureRoleCanBeAssigned(input.roleName);\n\n    await runMutation(\"create-role\", async () => {\n      ensureActiveOrganizationSelected();\n      const { response, payload } = await requestJson(\n        \"/v1/roles\",\n        {\n          method: \"POST\",\n          body: JSON.stringify(input),\n        },\n        12000,\n      );\n\n      if (!response.ok) {\n        throw getRequestError(payload, response, `Failed to create role (${response.status}).`);\n      }\n    });","sourceCodeStart":726,"sourceCodeEnd":762,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-web/app/(den)/dashboard/_providers/org-dashboard-provider.tsx#L726-L762","documentation":"createRole is gated by access.canManageRoles — only workspace owners and super-admins may define custom roles. When the flag is false the provider throws \"Only workspace owners and super-admins can manage roles.\" before ensureRoleCanBeAssigned and the API call. It is the client-side authorization gate for creating custom organization roles.","triggerScenarios":"Calling createRole({ roleName, permission }) (e.g. from roleForm in the roles screen) while the current member's resolved access lacks canManageRoles.","commonSituations":"A plain admin or member opens the roles screen via direct URL and submits the create-role form; role screens rendered without checking canManageRoles; stale orgContext after the member's role was downgraded.","solutions":["Ask an owner or super-admin to create the role.","Verify your role — only owner/super-admin satisfy canManageRoles.","Refresh org context if you were recently promoted, then retry.","Gate the roleForm and roles screen behind access.canManageRoles."],"exampleFix":"// before\n<RoleForm onSubmit={createRole} />\n\n// after\n{access.canManageRoles ? (\n  <RoleForm onSubmit={createRole} />\n) : (\n  <p>Only workspace owners and super-admins can manage roles.</p>\n)}","handlingStrategy":"validation","validationCode":"if (!access.canManageRoles) return; // gate roleForm submission","typeGuard":null,"tryCatchPattern":"try {\n  await createRole({ roleName, permission });\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"manage roles\")) {\n    toast(e.message);\n  } else throw e;\n}","preventionTips":["Show the create-role form only when access.canManageRoles is true.","Avoid exposing the roles screen URL to non owner/super-admin members.","Revalidate access flags on mount of the roles screen."],"tags":["authorization","rbac","roles"],"backgroundTag":"insufficient-permissions","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}