passbolt/passbolt_api · error · ValidationException
Cannot update group user.
Error message
Cannot update group user.
What it means
GroupsUsersUpdateService::assertAtLeastOneGroupManager throws this ValidationException when updating a group user (e.g. demoting is_admin to false) would leave the group without any group manager. The detailed message is set on the entity's is_admin field as at_least_one_group_manager.
Solutions
- Promote another member to is_admin=true first, then demote this user.
- Omit is_admin from the update payload if the demotion is unintended.
- Verify group manager count via GET /groups/{id} before patching.
- If the goal is ownership transfer, do it in one planned sequence: promote, then demote.
Example fix
// before
await passbolt.updateGroupUser(soleManagerId, { is_admin: false });
// after
await passbolt.updateGroupUser(otherMemberId, { is_admin: true });
await passbolt.updateGroupUser(soleManagerId, { is_admin: false }); Defensive patterns
Strategy: validation
Validate before calling
const managers = group.memberships.filter(m => m.is_admin);
if (payload.is_admin === false && managers.length === 1 && managers[0].id === id) return reject('last group manager'); Try / catch
try { await updateGroupUser(id, payload); } catch (e) { if (e.body?.errors?.is_admin?.at_least_one_group_manager) {...} } Prevention
- Guard is_admin=false updates on sole managers
- Transfer management before demotion
- Verify manager count via GET /groups/{id}
When it happens
Trigger: PUT /groups/users/{id} with data setting is_admin=false on the only group manager of the group (admin count after change would be 0).
Common situations: Demoting the sole manager to a normal member, scripts syncing group roles that flip is_admin without checking counts, UI edits in groups with a single manager.
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 delete 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/69bafc86df86d856.
Report an issue: GitHub.
Appendix: source
Thrown at src/Service/GroupsUsers/GroupsUsersUpdateService.php:84
* @throws \App\Error\Exception\ValidationException Cannot delete the last group manager.
*/
private function assertAtLeastOneGroupManager(GroupsUser $groupUser, array $data): void
{
$isAdmin = Hash::get($data, 'is_admin');
if ($isAdmin || is_null($isAdmin)) {
return;
}
if ($groupUser->is_admin === $isAdmin) {
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 update group user.', $groupUser);
}
}
/**
* Patch the group user with the data to update.
*
* @param \App\Utility\UserAccessControl $uac The user at the origin of the operation
* @param \App\Model\Entity\GroupsUser $groupUser The group user to update.
* @param array $data The date to use to patch the group user
* @return \App\Model\Entity\GroupsUser
*/
private function patchGroupUserEntity(UserAccessControl $uac, GroupsUser $groupUser, array $data): GroupsUser
{
$patchEntityOptions = ['accessibleFields' => ['is_admin' => true, 'modified_by' => true]];
$groupUser = $this->groupsUsersTable->patchEntity($groupUser, $data, $patchEntityOptions);
if ($groupUser->hasErrors()) {
$this->handleValidationErrors($groupUser);
}View on GitHub (pinned to 31c1bbc10f)