{"record":{"id":"b5ea2f94a4eef43a","repo":"passbolt/passbolt_api","slug":"the-user-cannot-be-deleted","errorCode":null,"errorMessage":"The user cannot be deleted.","messagePattern":"The user cannot be deleted\\.","errorType":"validation","errorClass":"App\\Error\\Exception\\CustomValidationException","httpStatus":422,"severity":"error","filePath":"src/Controller/Users/UsersDeleteController.php","lineNumber":259,"sourceCode":"                        $foldersTable = TableRegistry::getTableLocator()->get('Passbolt/Folders.Folders');\n                        $folders = $foldersTable->findIndex($user->id, $findFoldersOptions);\n                        $body['errors']['folders']['sole_owner'] = $folders;\n                        $msg .= ' ' . $errors['id']['soleOwnerOfSharedContent'];\n                    }\n                }\n            }\n\n            $groupsToDeleteIds = $this->GroupsUsers\n                ->findGroupsWhereUserOnlyMember($user->id)\n                ->all()\n                ->extract('group_id')\n                ->toArray();\n            if ($groupsToDeleteIds) {\n                $groupsToDelete = $this->Groups->findAllByIds($groupsToDeleteIds);\n                $body['groups_to_delete'] = $groupsToDelete;\n            }\n\n            throw new CustomValidationException($msg, $body);\n        }\n    }\n\n    /**\n     * Transfer the group managers which blocked the user delete\n     *\n     * @param \\App\\Model\\Entity\\User $user entity\n     * @throws \\Cake\\Http\\Exception\\BadRequestException The groups that required a change are not all satisfied\n     * @return void\n     */\n    protected function _transferGroupsManagers(User $user)\n    {\n        $managers = $this->request->getData('transfer.managers');\n        if (empty($managers)) {\n            return;\n        }\n\n        $groupsUsersIdsToUpdate = Hash::extract($managers, '{n}.id');","sourceCodeStart":241,"sourceCodeEnd":277,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/src/Controller/Users/UsersDeleteController.php#L241-L277","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Read the exception body: it lists sole-managed groups and solely-owned resources/folders","Transfer group management: POST /groups/<id> with owners/managers changes including the new owners","Transfer resource/folder ownership: PUT /share/<resourceId> or folder share endpoint adding another owner","Retry the delete once all transfers are done; use the dry-run (?dry-run=true) to re-check first"],"exampleFix":"// before\ndeleteUser(userId); // 422: The user cannot be deleted.\n// after\nawait shareResource(resourceId, { aro: newOwnerId, type: 15 }); // OWNER\nawait editGroup(groupId, { owners: [newManagerId] });\ndeleteUser(userId);","handlingStrategy":"try-catch","validationCode":"const dry = await api.deleteUser(id, { dryRun: true });\nif (dry.blockingGroups?.length || dry.blockingResources?.length) await transferOwnership(dry);","typeGuard":null,"tryCatchPattern":"try { await api.deleteUser(id); } catch (e) { if (e.status === 422 && e.body?.errors) { await handleTransfer(e.body.errors); return retryDelete(id); } throw e; }","preventionTips":["Always run the dry-run first and inspect the returned blocking entities","Transfer group management and resource/folder ownership before offboarding deletes","Never leave a user as sole owner of shared content in onboarding policies"],"tags":["validation","business-rules","user-management"],"backgroundTag":"schema-validation-failed","analyzedSha":"31c1bbc10f32808a607fa9bd81891e898779c0bc","analyzedAt":"2026-09-17T00:04:38.960Z","contentChangedAt":"2026-09-17T00:04:38.960Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}