danny-avila/LibreChat · error · Error
Principal ID is required for user, group, and role principal
Error message
Principal ID is required for user, group, and role principals
What it means
grantPermission requires a non-empty principalId for every principalType except PUBLIC. The check fires when principalType is USER, GROUP, or ROLE but principalId is null, undefined, or otherwise falsy. PUBLIC is the only principal that may omit it.
Source
Thrown at packages/api/src/acl/accessControlService.ts:66
expiredAt?: Date;
}): Promise<IAclEntry | null> {
const {
principalType,
principalId,
resourceType,
resourceId,
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)) {View on GitHub (pinned to 5ff282f900)
Solutions
- Ensure the caller resolves and passes principalId for USER/GROUP/ROLE grants.
- If you genuinely want everyone, switch principalType to PrincipalType.PUBLIC (no id).
- Add a precondition check in the route handler so the request returns 400 before reaching the service.
Example fix
// before
await grantPermission({ principalType: PrincipalType.USER, principalId: null, ... });
// after
await grantPermission({ principalType: PrincipalType.PUBLIC, ... });
// or
await grantPermission({ principalType: PrincipalType.USER, principalId: resolvedUserId, ... }); Defensive patterns
Strategy: validation
Validate before calling
import { PrincipalType } from 'librechat-data-provider';
function resolvePrincipalArgs(p: { principalType: PrincipalType; principalId?: string | null }) {
if (p.principalType !== PrincipalType.PUBLIC && !p.principalId) {
throw new Error('principalId is required for non-PUBLIC principals');
}
return p;
} Type guard
import { PrincipalType } from 'librechat-data-provider';
const needsPrincipalId = (t: PrincipalType): boolean => t !== PrincipalType.PUBLIC; Prevention
- Resolve principalId in the route handler and return 400 if it is missing for USER/GROUP/ROLE.
- Use PrincipalType.PUBLIC explicitly when you intend everyone.
When it happens
Trigger: grantPermission called with principalType: PrincipalType.USER and principalId omitted/null/undefined; passing PrincipalType.PUBLIC's id-less shape to a USER grant; a form or API payload where the user/group selector returned no selection.
Common situations: UI bug where the user picker was skipped; a migration script that iterates principals but encounters a null foreign key; misusing a PUBLIC grant flow for a specific user.
Related errors
- Invalid resourceType: ${resourceType}. Valid types: ${validT
- User principal not found
- Group principal not found
- Invalid principal type: ${principalType}
- Principal ID is required for user, group, and role principal
AI-assisted analysis of danny-avila/LibreChat@5ff282f900 (2026-08-12).
Data as JSON: /api/errors/aa60a1cf80cabfd7.
Report an issue: GitHub.