{"record":{"id":"06fe24229d7b4f51","repo":"passbolt/passbolt_api","slug":"the-role-does-not-exist-or-deleted-rolesupdateservice","errorCode":null,"errorMessage":"The role does not exist or deleted.","messagePattern":"The role does not exist or deleted\\.","errorType":"http","errorClass":"NotFoundException","httpStatus":404,"severity":"error","filePath":"src/Service/Roles/RolesUpdateService.php","lineNumber":96,"sourceCode":"            'role' => $result,\n            'oldName' => $oldName,\n        ]);\n\n        return $result;\n    }\n\n    /**\n     * @param string $roleId Role identifier to get.\n     * @return \\App\\Model\\Entity\\Role\n     * @throws \\Cake\\Http\\Exception\\NotFoundException If role doesn't exist in the database\n     */\n    private function getRole(string $roleId): Role\n    {\n        try {\n            /** @var \\App\\Model\\Entity\\Role $role */\n            $role = $this->Roles->find('notDeleted')->where(['id' => $roleId])->firstOrFail();\n        } catch (RecordNotFoundException $e) {\n            throw new NotFoundException(__('The role does not exist or deleted.'), null, $e);\n        }\n\n        return $role;\n    }\n\n    /**\n     * @param \\App\\Model\\Entity\\Role $role Role entity.\n     * @param array $data Data to patch.\n     * @param \\App\\Utility\\UserAccessControl $uac User Access Control.\n     * @return \\App\\Model\\Entity\\Role\n     */\n    private function patchRoleEntity(Role $role, array $data, UserAccessControl $uac): Role\n    {\n        $name = Hash::get($data, 'name', '');\n\n        $data = [\n            'name' => $name,\n            'modified_by' => $uac->getId(),","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/src/Service/Roles/RolesUpdateService.php#L78-L114","documentation":"RolesUpdateService::getRole throws this NotFoundException when no non-deleted role exists for the given ID (firstOrFail on the notDeleted finder raises RecordNotFoundException). It is surfaced as a 404 to the PUT /roles/{id} caller.","triggerScenarios":"PUT /roles/{uuid} where the UUID does not match any role, or matches a role already soft-deleted.","commonSituations":"Updating a role that was deleted in another session/tab, stale role ID from a cached list, double-submission after the first update deleted it, or a typo'd UUID.","solutions":["Confirm the role exists with GET /roles and use its current ID.","If the role was soft-deleted, recreate it via POST /roles instead of updating.","Invalidate/refetch any cached role listings in the client.","Verify the UUID is copied correctly (no truncation or encoding issues)."],"exampleFix":"// before\nPUT /roles/<deleted-role-id> -> 404 The role does not exist or deleted\n// after\nGET /roles  # pick valid id\nPUT /roles/<valid-id> {\"name\": \"new-name\"} -> 200","handlingStrategy":"try-catch","validationCode":"const roles = await api.listRoles();\nif (!roles.some(r => r.id === roleId)) {\n  throw new Error('Cannot update: role ' + roleId + ' does not exist');\n}","typeGuard":"function isUpdatableRole(roles, id) {\n  return Array.isArray(roles) && roles.some(r => r && r.id === id);\n}","tryCatchPattern":"try {\n  await api.updateRole(roleId, payload);\n} catch (e) {\n  if (e.status === 404 && /does not exist or deleted/i.test(e.message)) {\n    // refetch roles; recreate the role if it was soft-deleted\n  } else throw e;\n}","preventionTips":["Refetch GET /roles before updates instead of using cached IDs","Treat 404 on update as stale-state: refresh then recreate if needed","Validate the UUID before the call","Synchronize role lists across admin sessions/tabs"],"tags":["not-found","roles","soft-delete","passbolt"],"backgroundTag":"record-not-found","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"}