{"record":{"id":"9bfb6f9b825e501d","repo":"danny-avila/LibreChat","slug":"invalid-principal-type-principaltype-9bfb6f","errorCode":null,"errorMessage":"Invalid principal type: ${principalType}","messagePattern":"Invalid principal type: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/api/src/acl/accessControlService.ts","lineNumber":62,"sourceCode":"\n    grantedBy?: string | Types.ObjectId;\n    session?: ClientSession;\n    roleId?: string | Types.ObjectId;\n    expiredAt?: Date;\n  }): Promise<IAclEntry | null> {\n    const {\n      principalType,\n      principalId,\n      resourceType,\n      resourceId,\n      accessRoleId,\n      grantedBy,\n      session,\n      expiredAt,\n    } = args;\n    try {\n      if (!Object.values(PrincipalType).includes(principalType)) {\n        throw new Error(`Invalid principal type: ${principalType}`);\n      }\n\n      if (principalType !== PrincipalType.PUBLIC && !principalId) {\n        throw new Error('Principal ID is required for user, group, and role principals');\n      }\n\n      // Validate principalId based on type\n      if (principalId && principalType === PrincipalType.ROLE) {\n        // Role IDs are strings (role names)\n        if (typeof principalId !== 'string' || principalId.trim().length === 0) {\n          throw new Error(`Invalid role ID: ${principalId}`);\n        }\n      } else if (\n        principalType &&\n        principalType !== PrincipalType.PUBLIC &&\n        (!principalId || !Types.ObjectId.isValid(principalId))\n      ) {\n        // User and Group IDs must be valid ObjectIds","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/danny-avila/LibreChat/blob/5ff282f9006c436e561de1afd39a481bea1ef0d8/packages/api/src/acl/accessControlService.ts#L44-L80","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass a value imported directly from PrincipalType (e.g. PrincipalType.USER) rather than a string literal.","After upgrading librechat-data-provider, diff the PrincipalType enum and update all call sites.","Add a unit test that exercises grantPermission with every PrincipalType member to catch regressions."],"exampleFix":"// before\nawait grantPermission({ principalType: 'Users', ... });\n\n// after\nimport { PrincipalType } from 'librechat-data-provider';\nawait grantPermission({ principalType: PrincipalType.USER, ... });","handlingStrategy":"validation","validationCode":"import { PrincipalType } from 'librechat-data-provider';\n\nconst validPrincipalTypes = new Set(Object.values(PrincipalType));\nfunction assertPrincipalType(v: unknown): PrincipalType {\n  if (typeof v !== 'string' || !validPrincipalTypes.has(v as PrincipalType)) {\n    throw new Error(`Invalid principal type: ${String(v)}`);\n  }\n  return v as PrincipalType;\n}","typeGuard":"import { PrincipalType } from 'librechat-data-provider';\nconst isPrincipalType = (v: unknown): v is PrincipalType =>\n  typeof v === 'string' && Object.values(PrincipalType).includes(v as PrincipalType);","tryCatchPattern":null,"preventionTips":["Always import PrincipalType and reference its members; never inline string literals.","After upgrading librechat-data-provider, grep for PrincipalType usages and reconcile.","Add a type test that exercises grantPermission for every enum member."],"tags":["acl","validation","permissions","typescript","enum"],"backgroundTag":null,"analyzedSha":"5ff282f9006c436e561de1afd39a481bea1ef0d8","analyzedAt":"2026-08-12T21:38:08.145Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}