danny-avila/LibreChat · error · Error
Invalid role ID: ${principalId}
Error message
Invalid role ID: ${principalId} What it means
grantPermission() in PermissionService.js:88 throws this when principalType is ROLE and principalId is present but is not a non-empty string. Role principals use the role name as their identifier (not an ObjectId), so the service validates that it is a non-empty trimmed string.
Source
Thrown at api/server/services/PermissionService.js:88
resourceId,
accessRoleId,
grantedBy,
session,
}) => {
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 &&
!mongoose.Types.ObjectId.isValid(principalId)
) {
// User and Group IDs must be valid ObjectIds
throw new Error(`Invalid principal ID: ${principalId}`);
}
if (!resourceId || !mongoose.Types.ObjectId.isValid(resourceId)) {
throw new Error(`Invalid resource ID: ${resourceId}`);
}
validateResourceType(resourceType);
// Get the role to determine permission bits
const role = await db.findRoleByIdentifier(accessRoleId);View on GitHub (pinned to 5ff282f900)
Solutions
- For ROLE principals, pass the role name string (e.g. SystemRoles.ADMIN) as principalId.
- Separate the role-name field from the accessRoleId field in the calling form.
- Trim and validate non-empty on the client before submitting.
Example fix
// before
grantPermission({ principalType: PrincipalType.ROLE, principalId: ' ', ... });
// after
grantPermission({ principalType: PrincipalType.ROLE, principalId: roleName.trim(), ... }); Defensive patterns
Strategy: validation
Validate before calling
function assertRolePrincipalId(principalId) {
if (typeof principalId !== 'string' || principalId.trim().length === 0) {
throw new Error('ROLE principalId must be a non-empty role-name string');
}
} Type guard
const isRoleName = (id) => typeof id === 'string' && id.trim().length > 0;
Prevention
- Keep the role-name field visually distinct from ObjectId fields in the UI.
- Populate ROLE principalId from a known role list, never free-form text.
When it happens
Trigger: A caller passes principalType=ROLE with a principalId that is a number, an empty string, whitespace-only, null coerced to '', or an ObjectId. The role-name contract is violated.
Common situations: Frontend reused the user/group ObjectId field for a role grant. A form defaulted principalId to '' for role selection. Confusion between accessRoleId (the permission level) and the role principalId (the role name).
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/f0e3cd2fd5e71b8a.
Report an issue: GitHub.