{"record":{"id":"859b24d0ea2d49ad","repo":"passbolt/passbolt_api","slug":"the-role-could-not-be-deleted","errorCode":null,"errorMessage":"The role could not be deleted.","messagePattern":"The role could not be deleted\\.","errorType":"validation","errorClass":"CustomValidationException","httpStatus":400,"severity":"error","filePath":"src/Service/Roles/RolesDeleteService.php","lineNumber":100,"sourceCode":"     */\n    private function softDeleteRole(Role $role, UserAccessControl $uac): Role\n    {\n        $data = [\n            'deleted' => DateTime::now(),\n            'deleted_by' => $uac->getId(),\n        ];\n\n        $role = $this->Roles->patchEntity($role, $data, ['accessibleFields' => [\n            'deleted' => true,\n            'deleted_by' => true,\n        ]]);\n\n        try {\n            $result = $this->Roles->saveOrFail($role);\n        } catch (PersistenceFailedException $e) { // @phpstan-ignore-line\n            $errors = $e->getEntity()->getErrors();\n\n            throw new CustomValidationException(\n                __('The role could not be deleted.'),\n                $errors\n            );\n        } catch (Exception $e) {\n            throw new InternalErrorException(__('Could not delete the role, please try again later.'), null, $e);\n        }\n\n        return $result;\n    }\n}\n","sourceCodeStart":82,"sourceCodeEnd":111,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/src/Service/Roles/RolesDeleteService.php#L82-L111","documentation":"RolesDeleteService::softDeleteRole throws this CustomValidationException when saving the soft-delete flag fails model validation/rules (PersistenceFailedException), instead of an actual delete. Field-level errors are attached to the response.","triggerScenarios":"DELETE /roles/{id} where marking the role deleted violates a Roles table rule — e.g. a rulesChecker preventing deletion of a role still assigned to users or of the admin role.","commonSituations":"Attempting to delete built-in roles (admin, user, guest) that a rule protects, deleting a role that still has users associated, or concurrent modifications tripping optimistic-locking checks.","solutions":["Read the `errors` in the 400 response for the exact violated rule.","Move users off the role (reassign) before deleting it.","Do not attempt to delete built-in/system roles (admin, user) — these are protected.","Check RolesTable rules (src/Model/Table/RolesTable.php) for isDeletable-style rules."],"exampleFix":"// before\nDELETE /roles/<role-still-assigned> -> 400 The role could not be deleted\n// after\n# reassign users to another role first, then\nDELETE /roles/<empty-role-id> -> 200","handlingStrategy":"try-catch","validationCode":"const roles = await api.listRoles();\nconst role = roles.find(r => r.id === roleId);\nif (!role) throw new Error('Role not found');\nif (['admin', 'user', 'guest'].includes(role.name)) {\n  throw new Error('Built-in roles cannot be deleted');\n}","typeGuard":"function isDeletableRole(role) {\n  return role && typeof role.id === 'string' && !['admin', 'user', 'guest'].includes(role.name);\n}","tryCatchPattern":"try {\n  await api.deleteRole(roleId);\n} catch (e) {\n  if (e.status === 400 && e.body?.errors) {\n    // rule violation: reassign users or unprotect built-in role, then retry\n  } else throw e;\n}","preventionTips":["Never attempt to delete built-in roles (admin, user, guest)","Reassign users off the role before deleting","Check the RolesTable rules to know which roles are protected","Read the per-field `errors` in the 400 response"],"tags":["roles","validation","soft-delete","passbolt"],"backgroundTag":"database-write-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"}