{"record":{"id":"c58f895ab655e39f","repo":"danny-avila/LibreChat","slug":"role-accessroleid-not-found-c58f89","errorCode":null,"errorMessage":"Role ${accessRoleId} not found","messagePattern":"Role (.+?) not found","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/api/src/acl/accessControlService.ts","lineNumber":93,"sourceCode":"      } else if (\n        principalType &&\n        principalType !== PrincipalType.PUBLIC &&\n        (!principalId || !Types.ObjectId.isValid(principalId))\n      ) {\n        // User and Group IDs must be valid ObjectIds\n        throw new Error(`Invalid principal ID: ${principalId}`);\n      }\n\n      if (!resourceId || !Types.ObjectId.isValid(resourceId)) {\n        throw new Error(`Invalid resource ID: ${resourceId}`);\n      }\n\n      this.validateResourceType(resourceType as ResourceType);\n\n      // Get the role to determine permission bits\n      const role = await this._dbMethods.findRoleByIdentifier(accessRoleId);\n      if (!role) {\n        throw new Error(`Role ${accessRoleId} not found`);\n      }\n\n      // Ensure the role is for the correct resource type\n      if (role.resourceType !== resourceType) {\n        throw new Error(\n          `Role ${accessRoleId} is for ${role.resourceType} resources, not ${resourceType}`,\n        );\n      }\n      return await this._dbMethods.grantPermission(\n        principalType,\n        principalId,\n        resourceType,\n        resourceId,\n        role.permBits,\n        grantedBy,\n        session,\n        role._id,\n        expiredAt,","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/danny-avila/LibreChat/blob/5ff282f9006c436e561de1afd39a481bea1ef0d8/packages/api/src/acl/accessControlService.ts#L75-L111","documentation":"grantPermission looks up the role by its identifier via findRoleByIdentifier(accessRoleId). If no role document matches that identifier (e.g. AccessRoleIds.AGENT_VIEWER), the grant aborts because permBits cannot be determined. This is a referential-integrity check against the access roles collection.","triggerScenarios":"Passing an accessRoleId that does not exist in the roles collection; using a role identifier from a different resource type; a roles seed/migration that has not been run; a typo in the AccessRoleIds literal.","commonSituations":"Fresh database where role seeding was skipped; a renamed role identifier after an upgrade; passing the role's display name instead of its identifier; multi-tenant systems where the role belongs to another tenant.","solutions":["Run the access-role seed/migration so the role exists in the database.","Confirm the accessRoleId matches a value from AccessRoleIds exported by librechat-data-provider.","Verify the role's resourceType matches the resource you are granting on (see error 207)."],"exampleFix":"// before\nawait grantPermission({ ..., accessRoleId: 'AgentViewer' }); // wrong casing / display name\n\n// after\nimport { AccessRoleIds } from 'librechat-data-provider';\nawait grantPermission({ ..., accessRoleId: AccessRoleIds.AGENT_VIEWER });","handlingStrategy":"try-catch","validationCode":"import { AccessRoleIds } from 'librechat-data-provider';\nconst knownRoleIds = new Set(Object.values(AccessRoleIds));\nfunction assertAccessRole(v: string) {\n  if (!knownRoleIds.has(v)) throw new Error(`Unknown accessRoleId: ${v}`);\n  return v;\n}","typeGuard":"import { AccessRoleIds } from 'librechat-data-provider';\nconst isAccessRoleId = (v: unknown): v is string =>\n  typeof v === 'string' && Object.values(AccessRoleIds).includes(v as AccessRoleIds);","tryCatchPattern":"try {\n  await svc.grantPermission({ ..., accessRoleId });\n} catch (error) {\n  if (error instanceof Error && error.message.startsWith('Role ') && error.message.endsWith(' not found')) {\n    return res.status(400).json({ error: 'Unknown role; run the access-role seed.' });\n  }\n  throw error;\n}","preventionTips":["Run the access-role seed/migration on every deploy so roles exist.","Use AccessRoleIds constants rather than literal strings.","After upgrades, diff AccessRoleIds and re-seed if members changed."],"tags":["acl","validation","permissions","roles","data-integrity"],"backgroundTag":null,"analyzedSha":"5ff282f9006c436e561de1afd39a481bea1ef0d8","analyzedAt":"2026-08-12T21:38:08.145Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}