danny-avila/LibreChat · error · Error
Invalid role ID: ${principalId}
Error message
Invalid role ID: ${principalId} What it means
When principalType is ROLE, grantPermission expects principalId to be a non-empty trimmed string (the role name). This branch rejects role ids that are not strings or are whitespace-only. Role principals are keyed by name, not ObjectId, unlike USER and GROUP.
Source
Thrown at packages/api/src/acl/accessControlService.ts:73
accessRoleId,
grantedBy,
session,
expiredAt,
} = args;
try {
if (!Object.values(PrincipalType).includes(principalType)) {
throw new Error(`Invalid principal type: ${principalType}`);
}
if (principalType !== PrincipalType.PUBLIC && !principalId) {
throw new Error('Principal ID is required for user, group, and role principals');
}
// Validate principalId based on type
if (principalId && principalType === PrincipalType.ROLE) {
// Role IDs are strings (role names)
if (typeof principalId !== 'string' || principalId.trim().length === 0) {
throw new Error(`Invalid role ID: ${principalId}`);
}
} 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);View on GitHub (pinned to 5ff282f900)
Solutions
- Pass the role's logical identifier string (e.g. from AccessRoleIds or the role.name field), not its ObjectId.
- Trim and assert non-empty before calling grantPermission.
- Double-check principalType is genuinely ROLE; for USER/GROUP an ObjectId is correct.
Example fix
// before
await grantPermission({ principalType: PrincipalType.ROLE, principalId: roleDoc._id, ... });
// after
await grantPermission({ principalType: PrincipalType.ROLE, principalId: AccessRoleIds.MY_ROLE, ... }); Defensive patterns
Strategy: validation
Validate before calling
function assertRoleId(v: unknown): string {
if (typeof v !== 'string' || v.trim().length === 0) {
throw new Error(`Invalid role ID: ${String(v)}`);
}
return v;
} Type guard
const isRoleId = (v: unknown): v is string => typeof v === 'string' && v.trim().length > 0;
Prevention
- For ROLE principals pass the logical role name (AccessRoleIds), never an ObjectId.
- Trim and assert non-empty at the call site.
When it happens
Trigger: grantPermission with principalType: PrincipalType.ROLE where principalId is a number, an ObjectId, undefined, or a blank string; passing a role document's _id instead of its identifier string.
Common situations: Confusing the role's Mongo _id with its logical identifier (AccessRoleIds); copy-paste from a USER grant that supplies an ObjectId; trimming logic upstream that produced an empty string.
Related errors
- Invalid role ID: ${principalId}
- Role ${accessRoleId} not found
- 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/325610b657999960.
Report an issue: GitHub.