danny-avila/LibreChat · error · Error

Invalid principal ID: ${principalId}

Error message

Invalid principal ID: ${principalId}

What it means

For USER and GROUP principals (anything that is not PUBLIC and not ROLE), grantPermission requires principalId to be a value that passes mongoose Types.ObjectId.isValid. The check fires when the id is missing or not a 24-hex-char ObjectId string.

Source

Thrown at packages/api/src/acl/accessControlService.ts:81

      }

      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);
      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

  1. Pass the Mongo ObjectId of the user or group (typically req.user.userId or the group document's _id toString()).
  2. Validate the id format at the API boundary with Types.ObjectId.isValid before calling grantPermission.
  3. If your system uses non-ObjectId identifiers, map them to ObjectIds first.

Example fix

// before
await grantPermission({ principalType: PrincipalType.USER, principalId: req.user.email, ... });

// after
import { Types } from 'mongoose';
const userId = req.user.userId;
if (!Types.ObjectId.isValid(userId)) throw new Error('bad user id');
await grantPermission({ principalType: PrincipalType.USER, principalId: userId, ... });
Defensive patterns

Strategy: validation

Validate before calling

import { Types } from 'mongoose';

function assertObjectId(v: unknown): string {
  if (typeof v !== 'string' || !Types.ObjectId.isValid(v)) {
    throw new Error(`Invalid principal ID: ${String(v)}`);
  }
  return v;
}

Type guard

import { Types } from 'mongoose';
const isObjectId = (v: unknown): v is string =>
  typeof v === 'string' && Types.ObjectId.isValid(v);

Prevention

When it happens

Trigger: grantPermission with principalType USER/GROUP and principalId that is undefined, a non-hex string, an email address, a username, or a malformed id from the client.

Common situations: Passing req.user.username or email instead of req.user.userId; a client sending a uuid/v4 instead of a Mongo ObjectId; string slicing that truncated the id.

Related errors


AI-assisted analysis of danny-avila/LibreChat@5ff282f900 (2026-08-12). Data as JSON: /api/errors/eef57abf443f6737. Report an issue: GitHub.