{"record":{"id":"75352851fe891ee7","repo":"different-ai/openwork","slug":"only-workspace-admins-can-invite-members","errorCode":null,"errorMessage":"Only workspace admins can invite members.","messagePattern":"Only workspace admins can invite members\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ee/apps/den-web/app/(den)/dashboard/_providers/org-dashboard-provider.tsx","lineNumber":587,"sourceCode":"\n    await runReauthableAction(\"delete-organization\", async () => {\n      ensureActiveOrganizationSelected();\n      const { response, payload } = await requestJson(\n        \"/v1/org\",\n        { method: \"DELETE\" },\n        12000,\n      );\n\n      if (!response.ok) {\n        throw getRequestError(payload, response, `Failed to delete organization (${response.status}).`);\n      }\n    });\n  }\n\n  async function inviteMember(input: { email: string; role: string }) {\n    const access = getCurrentAccess();\n    if (!access.canInviteMembers) {\n      throw new Error(\"Only workspace admins can invite members.\");\n    }\n    const invitationRole = access.canManageRoles ? input.role : \"member\";\n    ensureRoleCanBeAssigned(invitationRole);\n\n    await runMutation(\"invite-member\", async () => {\n      ensureActiveOrganizationSelected();\n      const { response, payload } = await requestJson(\n        \"/v1/invitations\",\n        {\n          method: \"POST\",\n          body: JSON.stringify({ email: input.email, role: invitationRole }),\n        },\n        12000,\n      );\n\n      if (!response.ok) {\n        const paymentRequiredError = getOrgPaymentRequiredError(payload);\n        if (paymentRequiredError) {","sourceCodeStart":569,"sourceCodeEnd":605,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-web/app/(den)/dashboard/_providers/org-dashboard-provider.tsx#L569-L605","documentation":"inviteMember checks the current member's access flags (derived from role, isOwner, and custom roles via getOrgAccessFlags) before allowing an invitation. If access.canInviteMembers is false the provider throws \"Only workspace admins can invite members.\" instead of calling the invitation API. It is an intentional client-side authorization gate mirroring server-side RBAC.","triggerScenarios":"Calling inviteMember({ email, role }) while the signed-in member's role resolves to a non-admin (e.g. plain \"member\") in the active organization, so canInviteMembers is false.","commonSituations":"A regular member reaches the Manage Members UI (e.g. via stale navigation or a shared link) and submits the invite form; a role downgrade happened server-side and orgContext still shows old privileges; the wrong organization is active.","solutions":["Ask a workspace owner, super-admin, or admin to send the invitation.","Verify the current member's role in the active organization (Settings → Members).","Refresh org context so role changes are reflected, then retry if the member was actually promoted.","Hide the invite UI for members lacking canInviteMembers instead of allowing the attempt."],"exampleFix":"// before\nawait inviteMember({ email, role });\n\n// after\nif (!getCurrentAccess().canInviteMembers) {\n  showNotice(\"Only workspace admins can invite members.\");\n  return;\n}\nawait inviteMember({ email, role });","handlingStrategy":"try-catch","validationCode":"if (!access.canInviteMembers) {\n  // hide/disable invite form before calling inviteMember\n}","typeGuard":null,"tryCatchPattern":"try {\n  await inviteMember({ email, role });\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith(\"Only workspace admins\")) {\n    setPermissionError(e.message);\n  } else throw e;\n}","preventionTips":["Render invite UI only when access.canInviteMembers is true.","Keep orgContext fresh after role changes.","Surface role requirements in the UI before submission."],"tags":["authorization","rbac","permissions"],"backgroundTag":"insufficient-permissions","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}