danny-avila/LibreChat · error · Error
Role ${accessRoleId} not found
Error message
Role ${accessRoleId} not found What it means
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.
Source
Thrown at packages/api/src/acl/accessControlService.ts:93
} else if (
principalType &&
principalType !== PrincipalType.PUBLIC &&
(!principalId || !Types.ObjectId.isValid(principalId))
) {
// User and Group IDs must be valid ObjectIds
throw new Error(`Invalid principal ID: ${principalId}`);
}
if (!resourceId || !Types.ObjectId.isValid(resourceId)) {
throw new Error(`Invalid resource ID: ${resourceId}`);
}
this.validateResourceType(resourceType as ResourceType);
// Get the role to determine permission bits
const role = await this._dbMethods.findRoleByIdentifier(accessRoleId);
if (!role) {
throw new Error(`Role ${accessRoleId} not found`);
}
// Ensure the role is for the correct resource type
if (role.resourceType !== resourceType) {
throw new Error(
`Role ${accessRoleId} is for ${role.resourceType} resources, not ${resourceType}`,
);
}
return await this._dbMethods.grantPermission(
principalType,
principalId,
resourceType,
resourceId,
role.permBits,
grantedBy,
session,
role._id,
expiredAt,View on GitHub (pinned to 5ff282f900)
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).
Example fix
// before
await grantPermission({ ..., accessRoleId: 'AgentViewer' }); // wrong casing / display name
// after
import { AccessRoleIds } from 'librechat-data-provider';
await grantPermission({ ..., accessRoleId: AccessRoleIds.AGENT_VIEWER }); Defensive patterns
Strategy: try-catch
Validate before calling
import { AccessRoleIds } from 'librechat-data-provider';
const knownRoleIds = new Set(Object.values(AccessRoleIds));
function assertAccessRole(v: string) {
if (!knownRoleIds.has(v)) throw new Error(`Unknown accessRoleId: ${v}`);
return v;
} Type guard
import { AccessRoleIds } from 'librechat-data-provider';
const isAccessRoleId = (v: unknown): v is string =>
typeof v === 'string' && Object.values(AccessRoleIds).includes(v as AccessRoleIds); Try / catch
try {
await svc.grantPermission({ ..., accessRoleId });
} catch (error) {
if (error instanceof Error && error.message.startsWith('Role ') && error.message.endsWith(' not found')) {
return res.status(400).json({ error: 'Unknown role; run the access-role seed.' });
}
throw error;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Invalid role ID: ${principalId}
- Invalid role ID: ${principalId}
- Role ${accessRoleId} is for ${role.resourceType} resources,
- Invalid resourceType: ${resourceType}. Valid types: ${validT
- User principal not found
AI-assisted analysis of danny-avila/LibreChat@5ff282f900 (2026-08-12).
Data as JSON: /api/errors/c58f895ab655e39f.
Report an issue: GitHub.