{"record":{"id":"cb44516cb693dbf1","repo":"different-ai/openwork","slug":"ownership-can-only-be-transferred-to-an-active-sup","errorCode":null,"errorMessage":"Ownership can only be transferred to an active super-admin.","messagePattern":"Ownership can only be transferred to an active super-admin\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"ee/apps/den-web/app/(den)/dashboard/_providers/org-dashboard-provider.tsx","lineNumber":725,"sourceCode":"        `/v1/members/${encodeURIComponent(memberId)}`,\n        { method: \"DELETE\" },\n        12000,\n      );\n\n      if (response.status !== 204 && !response.ok) {\n        throw getRequestError(payload, response, `Failed to remove member (${response.status}).`);\n      }\n    });\n  }\n\n  async function transferOwnership(memberId: string) {\n    if (!getCurrentAccess().canTransferOwnership) {\n      throw new Error(\"Only the workspace owner can transfer ownership.\");\n    }\n    const target = ensureTargetIsNotOwner(memberId);\n    const targetAccess = getOrgAccessFlags(target?.role ?? \"member\", target?.isOwner ?? false, orgContext?.roles);\n    if (!target || !target.joinedAt || !targetAccess.isSuperAdmin) {\n      throw new Error(\"Ownership can only be transferred to an active super-admin.\");\n    }\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) {","sourceCodeStart":707,"sourceCodeEnd":743,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/ee/apps/den-web/app/(den)/dashboard/_providers/org-dashboard-provider.tsx#L707-L743","documentation":"After the owner check, transferOwnership validates the target member: the member must exist in orgContext.members, have a joinedAt timestamp (active member), and resolve to a super-admin role via getOrgAccessFlags. If any of these fail, the provider throws \"Ownership can only be transferred to an active super-admin.\" This enforces that ownership only passes to an active super-admin, never to a pending invite, non-member, or lower role.","triggerScenarios":"Calling transferOwnership with a memberId that is not in the member list, belongs to a member who has not joined (no joinedAt), or whose role is not a super-admin (e.g. member/admin/custom role).","commonSituations":"Selecting a pending invitation as the transfer target; attempting to promote a regular member directly to owner in one step (must first grant super-admin); memberId from a stale member list after the target left the org; passing a wrong/typo'd id programmatically.","solutions":["Promote the target to super-admin first, then transfer ownership.","Only transfer to a member who has accepted their invite (joinedAt set).","Re-check the member list and use a fresh memberId if the target recently joined or left.","Filter the transfer UI to active super-admins only."],"exampleFix":"// before\nhandleTransferOwnership(inviteeId); // pending invite\n\n// after\nconst target = members.find((m) => m.id === inviteeId);\nconst ok = target?.joinedAt && getOrgAccessFlags(target.role, target.isOwner, roles).isSuperAdmin;\nif (!ok) {\n  showError(\"Ownership can only be transferred to an active super-admin.\");\n  return;\n}\nhandleTransferOwnership(inviteeId);","handlingStrategy":"validation","validationCode":"const target = members.find((m) => m.id === memberId);\nconst eligible = !!target?.joinedAt && getOrgAccessFlags(target.role, target.isOwner, roles).isSuperAdmin;\nif (!eligible) { /* block call: promote to super-admin first */ }","typeGuard":"function isEligibleOwnerTarget(m: Member | undefined, roles: OrgRole[]): m is Member & { joinedAt: string } {\n  return !!m && !!m.joinedAt && !m.isOwner && getOrgAccessFlags(m.role, m.isOwner, roles).isSuperAdmin;\n}","tryCatchPattern":"try {\n  await transferOwnership(memberId);\n} catch (e) {\n  if (e instanceof Error && e.message.includes(\"active super-admin\")) {\n    showNotice(\"Promote the member to super-admin before transferring ownership.\");\n  } else throw e;\n}","preventionTips":["Only list active super-admins in the transfer-target picker.","Exclude pending invitations (no joinedAt) from targets.","Grant super-admin first, then transfer ownership in two steps."],"tags":["validation","rbac","ownership"],"backgroundTag":"invalid-transfer-target","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}