passbolt/passbolt_api · error · App\Error\Exception\CustomValidationException

The user cannot be deleted.

Error message

The user cannot be deleted.

What it means

Before deleting, the controller runs CakePHP's delete rules on the user (checkRules with RulesChecker::DELETE). If any business rule fails — typically the user is sole manager of a non-empty group, or sole owner of shared resources (and folders when the plugin is enabled) — a CustomValidationException is thrown with a body enumerating the blocking groups/resources/folders. The deletion is refused so shared content does not become orphaned.

Solutions

  1. Read the exception body: it lists sole-managed groups and solely-owned resources/folders
  2. Transfer group management: POST /groups/<id> with owners/managers changes including the new owners
  3. Transfer resource/folder ownership: PUT /share/<resourceId> or folder share endpoint adding another owner
  4. Retry the delete once all transfers are done; use the dry-run (?dry-run=true) to re-check first

Example fix

// before
deleteUser(userId); // 422: The user cannot be deleted.
// after
await shareResource(resourceId, { aro: newOwnerId, type: 15 }); // OWNER
await editGroup(groupId, { owners: [newManagerId] });
deleteUser(userId);
Defensive patterns

Strategy: try-catch

Validate before calling

const dry = await api.deleteUser(id, { dryRun: true });
if (dry.blockingGroups?.length || dry.blockingResources?.length) await transferOwnership(dry);

Try / catch

try { await api.deleteUser(id); } catch (e) { if (e.status === 422 && e.body?.errors) { await handleTransfer(e.body.errors); return retryDelete(id); } throw e; }

Prevention

When it happens

Trigger: DELETE /users/<id> where the target user is the only group manager of a group with other members, or the only owner of shared resources/folders, without first transferring ownership; the response body lists the offending entities under errors.groups/errors.resources/errors.folders.

Common situations: Offboarding an employee who created all the team passwords; deleting a user who administrates shared groups; running the delete dry-run and then calling delete without handling the transfer payload.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17). Data as JSON: /api/errors/b5ea2f94a4eef43a. Report an issue: GitHub.

Appendix: source

Thrown at src/Controller/Users/UsersDeleteController.php:259

                        $foldersTable = TableRegistry::getTableLocator()->get('Passbolt/Folders.Folders');
                        $folders = $foldersTable->findIndex($user->id, $findFoldersOptions);
                        $body['errors']['folders']['sole_owner'] = $folders;
                        $msg .= ' ' . $errors['id']['soleOwnerOfSharedContent'];
                    }
                }
            }

            $groupsToDeleteIds = $this->GroupsUsers
                ->findGroupsWhereUserOnlyMember($user->id)
                ->all()
                ->extract('group_id')
                ->toArray();
            if ($groupsToDeleteIds) {
                $groupsToDelete = $this->Groups->findAllByIds($groupsToDeleteIds);
                $body['groups_to_delete'] = $groupsToDelete;
            }

            throw new CustomValidationException($msg, $body);
        }
    }

    /**
     * Transfer the group managers which blocked the user delete
     *
     * @param \App\Model\Entity\User $user entity
     * @throws \Cake\Http\Exception\BadRequestException The groups that required a change are not all satisfied
     * @return void
     */
    protected function _transferGroupsManagers(User $user)
    {
        $managers = $this->request->getData('transfer.managers');
        if (empty($managers)) {
            return;
        }

        $groupsUsersIdsToUpdate = Hash::extract($managers, '{n}.id');

View on GitHub (pinned to 31c1bbc10f)