{"record":{"id":"03a7bbbb714449fd","repo":"RocketChat/Rocket.Chat","slug":"error-role-not-found","errorCode":"error-role-not-found","errorMessage":"Role not found","messagePattern":"Role not found","errorType":"exception","errorClass":"MeteorError","httpStatus":null,"severity":"error","filePath":"apps/meteor/ee/server/lib/roles/updateRole.ts","lineNumber":58,"sourceCode":"\t} else {\n\t\troleData.scope = role.scope;\n\t}\n\n\tawait Roles.updateById(roleId, roleData.name, roleData.scope, roleData.description, roleData.mandatory2fa);\n\n\tvoid notifyOnRoleChangedById(roleId);\n\n\tif (options.broadcastUpdate) {\n\t\tvoid api.broadcast('user.roleUpdate', {\n\t\t\ttype: 'changed',\n\t\t\t_id: roleId,\n\t\t\tscope: roleData.scope,\n\t\t});\n\t}\n\n\tconst updatedRole = await Roles.findOneById(roleId);\n\tif (!updatedRole) {\n\t\tthrow new MeteorError('error-role-not-found', 'Role not found');\n\t}\n\n\treturn updatedRole;\n};\n","sourceCodeStart":40,"sourceCodeEnd":63,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0/apps/meteor/ee/server/lib/roles/updateRole.ts#L40-L63","documentation":"After Roles.updateById succeeds and change notifications are broadcast, updateRole re-fetches the role; if findOneById now returns null it throws MeteorError('error-role-not-found', 'Role not found'). Because the role existed at the start of the function (checked at the top), this error signals the role was deleted concurrently between the initial lookup and the post-update re-fetch - a race, not a bad request.","triggerScenarios":"Two admins (or admin UI plus an API script) operating on the same role: one calls updateRole while the other deletes the role mid-flight; the update path completes its write and side effects, then fails to re-read the now-deleted document.","commonSituations":"Role management screens left open in multiple tabs; cleanup automation that removes 'unused' roles racing with manual edits; retrying an update against an id that was deleted between attempts.","solutions":["Treat it as a conflict: re-check whether the role still exists; if it was intentionally deleted, the update is moot and can be dropped.","If the update still matters, retry the whole operation against a role id that verifiably exists.","Serialize role administration (single operator or tool at a time) so delete and update cannot interleave."],"exampleFix":"// before\nconst updated = await updateRole(roleId, data); // throws error-role-not-found on a delete race\n\n// after\ntry {\n\tconst updated = await updateRole(roleId, data);\n} catch (e: any) {\n\tif (e?.error === 'error-role-not-found' && !(await Roles.findOneById(roleId))) {\n\t\treturn; // role deleted concurrently; nothing left to update\n\t}\n\tthrow e;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n\tconst updated = await updateRole(roleId, roleData);\n} catch (e: any) {\n\tif (e?.error === 'error-role-not-found' && !(await Roles.findOneById(roleId))) {\n\t\treturn; // deleted concurrently; the update is moot - reconcile and stop\n\t}\n\tthrow e;\n}","preventionTips":["Serialize role administration so a delete cannot interleave with an update.","Build admin UIs to re-check existence right before save and after errors.","Treat 'error-role-not-found' after a successful start-of-function lookup as a concurrency signal, not a bad request."],"tags":["roles","permissions","race-condition","entity-not-found","enterprise"],"backgroundTag":"entity-not-found","analyzedSha":"b2c16d5842cbe6b69b59bdf6fc5e5f1afcd1f0b0","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}