{"record":{"id":"a6d05f0e17672c26","repo":"toeverything/AFFiNE","slug":"new-owner-is-not-active-member","errorCode":"new_owner_is_not_active_member","errorMessage":"Can not set a non-active member as owner.","messagePattern":"Can not set a non-active member as owner\\.","errorType":"exception","errorClass":"NewOwnerIsNotActiveMember","httpStatus":400,"severity":"error","filePath":"packages/backend/server/src/models/workspace-user.ts","lineNumber":91,"sourceCode":"      },\n    });\n    const fallbackRole =\n      oldOwner && (await this.models.workspace.isTeamWorkspace(workspaceId))\n        ? WorkspaceRole.Admin\n        : WorkspaceRole.Collaborator;\n\n    try {\n      await this.models.workspaceMember.setOwner(\n        workspaceId,\n        userId,\n        fallbackRole\n      );\n    } catch (error) {\n      if (\n        error instanceof Error &&\n        error.message === 'New workspace owner must be an active member.'\n      ) {\n        throw new NewOwnerIsNotActiveMember();\n      }\n      throw error;\n    }\n\n    if (oldOwner?.user && oldOwner.user.id !== userId) {\n      this.event.emit('workspace.owner.changed', {\n        workspaceId,\n        from: oldOwner.user.id,\n        to: userId,\n      });\n      this.logger.log(\n        `Transfer workspace owner of [${workspaceId}] from [${oldOwner.user.id}] to [${userId}]`\n      );\n    } else {\n      this.logger.log(`Set workspace owner of [${workspaceId}] to [${userId}]`);\n    }\n  }\n","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/2af30773aecd567f09b346e7b72fc69143144057/packages/backend/server/src/models/workspace-user.ts#L73-L109","documentation":"WorkspaceUser ownership transfer delegates to workspaceMember.setOwner(); when that model rejects the target with the plain Error 'New workspace owner must be an active member.', this wrapper translates it to NewOwnerIsNotActiveMember. The designated owner must hold an ACTIVE membership in the workspace — pending invitees, revoked, or departed members do not qualify.","triggerScenarios":"Transferring workspace ownership to a userId whose membership in that workspace is not active — invitation not yet accepted, membership revoked, or member has left.","commonSituations":"UI member picker lists pending invitees as transfer targets; the chosen owner left the workspace between selection and confirmation; an admin revoked the membership concurrently.","solutions":["Refresh the member list and choose a member whose membership status is active before confirming the transfer.","If the intended owner departed or was revoked, re-invite them and wait for acceptance, then transfer.","Check the target's membership record (status/role) before calling the transfer API."],"exampleFix":"// before\nawait workspaceUser.transferOwner(workspaceId, pendingInviteeId, 'admin'); // invite not accepted -> NewOwnerIsNotActiveMember\n\n// after\nconst member = await getMember(workspaceId, newOwnerId);\nif (!member || member.status !== 'active') {\n  throw new Error('New owner must be an active member.');\n}\nawait workspaceUser.transferOwner(workspaceId, newOwnerId, 'admin');","handlingStrategy":"validation","validationCode":"const membership = await models.workspaceMember.get(workspaceId, newOwnerId);\nif (!membership || membership.status !== 'active') {\n  throw new Error('New owner must be an active member.');\n}\nawait workspaceUser.transferOwner(workspaceId, newOwnerId, fallbackRole);","typeGuard":"function isActiveMember(m: { status?: string } | null | undefined): m is { status: 'active' } {\n  return !!m && m.status === 'active';\n}","tryCatchPattern":"try {\n  await workspaceUser.transferOwner(workspaceId, newOwnerId, 'admin');\n} catch (e) {\n  if (e instanceof NewOwnerIsNotActiveMember) {\n    // re-invite the user or pick another member\n  } else throw e;\n}","preventionTips":["Filter transfer-target pickers to active memberships only.","Re-fetch the member list right before confirming a transfer.","Remember the underlying setOwner requires active membership — pending invitees never qualify."],"tags":["workspace","ownership","membership","transfer"],"backgroundTag":"ownership-transfer-invalid-target","analyzedSha":"2af30773aecd567f09b346e7b72fc69143144057","analyzedAt":"2026-08-18T21:16:52.546Z","contentChangedAt":"2026-08-18T21:16:52.546Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}