danny-avila/LibreChat · error · Error
Invalid principal ID: ${principalId}
Error message
Invalid principal ID: ${principalId} What it means
grantPermission() in PermissionService.js:96 throws this for USER and GROUP principals when principalId is present but is not a valid MongoDB ObjectId (mongoose.Types.ObjectId.isValid returns false). This guards the ACL store against malformed ids before the existence lookup.
Source
Thrown at api/server/services/PermissionService.js:96
}
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);
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}`,View on GitHub (pinned to 5ff282f900)
Solutions
- Resolve usernames/emails to the user's Mongo _id before calling grantPermission.
- For USER/GROUP, pass the full 24-char ObjectId hex string from the user/group record.
- Validate the id shape on the client (24 hex chars) before submitting.
Example fix
// before
grantPermission({ principalType: PrincipalType.USER, principalId: 'alice@example.com', ... });
// after
const user = await db.findUser({ email: 'alice@example.com' }, '_id');
grantPermission({ principalType: PrincipalType.USER, principalId: user._id.toString(), ... }); Defensive patterns
Strategy: validation
Validate before calling
function assertObjectId(id) {
if (!mongoose.Types.ObjectId.isValid(id)) {
throw new Error(`not a valid ObjectId: ${id}`);
}
} Type guard
const isValidObjectId = (id) => mongoose.Types.ObjectId.isValid(id);
Prevention
- Resolve usernames/emails to Mongo _id before calling grantPermission.
- Validate the 24-hex-char shape on the client before submitting.
When it happens
Trigger: principalType is USER or GROUP and principalId is a non-ObjectId string (e.g. an email, a username, a UUID, or a truncated id). Fires before the user/group existence checks, so it specifically catches shape errors rather than missing records.
Common situations: Caller passes an email or username instead of the user _id. A UUID-based external id was used where Mongo ObjectId is required. A copy/paste truncated the 24-char hex string.
Related errors
- Invalid principal ID: ${principalId}
- Invalid resource ID: ${resourceId}
- Invalid resourceType: ${resourceType}. Valid types: ${validT
- User principal not found
- Group principal not found
AI-assisted analysis of danny-avila/LibreChat@5ff282f900 (2026-08-12).
Data as JSON: /api/errors/1eb36c3df312df20.
Report an issue: GitHub.