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

  1. Resolve usernames/emails to the user's Mongo _id before calling grantPermission.
  2. For USER/GROUP, pass the full 24-char ObjectId hex string from the user/group record.
  3. 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

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


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