passbolt/passbolt_api · error · ValidationException
Cannot delete group user.
Error message
Cannot delete group user.
What it means
GroupsUsersDeleteService::assertAtLeastOneGroupManager throws this ValidationException when deleting a group user would leave the group with zero group managers (i.e. the deleted user is the only is_admin member). The real reason is attached as a validation error on the entity's is_admin field.
Solutions
- Promote another group member to admin (PUT /groups/users/{id} with is_admin=true) before deleting this one.
- Delete a different member first if another manager exists.
- Check GET /groups/{id} memberships for other is_admin users before calling delete.
- If intending to dissolve the group, delete the whole group instead (DELETE /groups/{id}).
Example fix
// before
await passbolt.deleteGroupUser(lastManagerId);
// after
await passbolt.updateGroupUser(otherMemberId, { is_admin: true });
await passbolt.deleteGroupUser(lastManagerId); Defensive patterns
Strategy: validation
Validate before calling
const managers = group.memberships.filter(m => m.is_admin);
if (managers.length === 1 && managers[0].id === targetId) throw new Error('last group manager'); Try / catch
try { await deleteGroupUser(id); } catch (e) { if (e.body?.errors?.is_admin?.at_least_one_group_manager) {...} } Prevention
- Always check is_admin distribution before deleting memberships
- Promote a backup manager for large groups
When it happens
Trigger: DELETE /groups/users/{id} where the target group user is the sole admin of the group ($groupManagersCount === 1 and the user is that admin).
Common situations: Removing the last group manager before another member is promoted, bulk-cleanups of group memberships, deleting memberships via API without checking is_admin distribution.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Cannot update group user.
- group(s) returned by your directory are invalid and will be…
- Could not validate group data.
- Could not validate group data.
- Could not validate group data.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/f0bba5e41e283a3f.
Report an issue: GitHub.
Appendix: source
Thrown at src/Service/GroupsUsers/GroupsUsersDeleteService.php:114
* Assert that the group to remove the group user in will have at least one manager after removing the group user.
*
* @param \App\Model\Entity\GroupsUser $groupUser The group user to check the group for
* @return void
* @throws \App\Error\Exception\ValidationException Cannot delete the last group manager.
*/
private function assertAtLeastOneGroupManager(GroupsUser $groupUser): void
{
if (!$groupUser->is_admin) {
return;
}
$groupManagersCount = $this->groupsUsersTable->findByGroupIdAndIsAdmin($groupUser->group_id, true)
->all()
->count();
if ($groupManagersCount === 1) {
$groupUser->setError('is_admin', ['at_least_one_group_manager' => 'Cannot delete the last group manager.']);
throw new ValidationException('Cannot delete group user.', $groupUser);
}
}
/**
* Delete the secrets for the resources the user lost access after being removed from the group.
*
* @param \App\Model\Entity\GroupsUser $groupUser The group user to delete.
* @return array<\App\Model\Entity\Secret>
*/
private function deleteLostAccessAssociatedSecrets(GroupsUser $groupUser): array
{
$lostAccessSecretsConditions = [
'user_id' => $groupUser->user_id,
'resource_id IN' => $this->findLostAccessResourcesIdsQuery($groupUser),
];
/** @var array<\App\Model\Entity\Secret> $lostAccessSecrets */
$lostAccessSecrets = $this->secretsTable->find()
->select(['id', 'resource_id', 'user_id'])View on GitHub (pinned to 31c1bbc10f)