danny-avila/LibreChat · error · Error

Invalid principal type: ${principalType}

Error message

Invalid principal type: ${principalType}

What it means

grantPermission validates that principalType is one of the PrincipalType enum values (USER, GROUP, PUBLIC, ROLE). Any value outside that set is rejected before any database access. This guards the ACL entry creation path against malformed principal classifications.

Source

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

    grantedBy?: string | Types.ObjectId;
    session?: ClientSession;
    roleId?: string | Types.ObjectId;
    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

View on GitHub (pinned to 5ff282f900)

Solutions

  1. Pass a value imported directly from PrincipalType (e.g. PrincipalType.USER) rather than a string literal.
  2. After upgrading librechat-data-provider, diff the PrincipalType enum and update all call sites.
  3. Add a unit test that exercises grantPermission with every PrincipalType member to catch regressions.

Example fix

// before
await grantPermission({ principalType: 'Users', ... });

// after
import { PrincipalType } from 'librechat-data-provider';
await grantPermission({ principalType: PrincipalType.USER, ... });
Defensive patterns

Strategy: validation

Validate before calling

import { PrincipalType } from 'librechat-data-provider';

const validPrincipalTypes = new Set(Object.values(PrincipalType));
function assertPrincipalType(v: unknown): PrincipalType {
  if (typeof v !== 'string' || !validPrincipalTypes.has(v as PrincipalType)) {
    throw new Error(`Invalid principal type: ${String(v)}`);
  }
  return v as PrincipalType;
}

Type guard

import { PrincipalType } from 'librechat-data-provider';
const isPrincipalType = (v: unknown): v is PrincipalType =>
  typeof v === 'string' && Object.values(PrincipalType).includes(v as PrincipalType);

Prevention

When it happens

Trigger: Calling grantPermission with principalType set to a string not in PrincipalType, e.g. a typo like 'Users', a numeric id, undefined, or a stale enum value after a librechat-data-provider version bump that renamed members.

Common situations: A refactor passes the raw user role string instead of the PrincipalType enum; an upgrade to librechat-data-provider adds/removes enum members and a downstream caller still sends the old literal; deserialized JSON where the field is missing or misspelled.

Related errors


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