{"record":{"id":"eef57abf443f6737","repo":"danny-avila/LibreChat","slug":"invalid-principal-id-principalid-eef57a","errorCode":null,"errorMessage":"Invalid principal ID: ${principalId}","messagePattern":"Invalid principal ID: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/api/src/acl/accessControlService.ts","lineNumber":81,"sourceCode":"      }\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\n        throw new Error(`Invalid principal ID: ${principalId}`);\n      }\n\n      if (!resourceId || !Types.ObjectId.isValid(resourceId)) {\n        throw new Error(`Invalid resource ID: ${resourceId}`);\n      }\n\n      this.validateResourceType(resourceType as ResourceType);\n\n      // Get the role to determine permission bits\n      const role = await this._dbMethods.findRoleByIdentifier(accessRoleId);\n      if (!role) {\n        throw new Error(`Role ${accessRoleId} not found`);\n      }\n\n      // Ensure the role is for the correct resource type\n      if (role.resourceType !== resourceType) {\n        throw new Error(\n          `Role ${accessRoleId} is for ${role.resourceType} resources, not ${resourceType}`,","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/danny-avila/LibreChat/blob/5ff282f9006c436e561de1afd39a481bea1ef0d8/packages/api/src/acl/accessControlService.ts#L63-L99","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass the Mongo ObjectId of the user or group (typically req.user.userId or the group document's _id toString()).","Validate the id format at the API boundary with Types.ObjectId.isValid before calling grantPermission.","If your system uses non-ObjectId identifiers, map them to ObjectIds first."],"exampleFix":"// before\nawait grantPermission({ principalType: PrincipalType.USER, principalId: req.user.email, ... });\n\n// after\nimport { Types } from 'mongoose';\nconst userId = req.user.userId;\nif (!Types.ObjectId.isValid(userId)) throw new Error('bad user id');\nawait grantPermission({ principalType: PrincipalType.USER, principalId: userId, ... });","handlingStrategy":"validation","validationCode":"import { Types } from 'mongoose';\n\nfunction assertObjectId(v: unknown): string {\n  if (typeof v !== 'string' || !Types.ObjectId.isValid(v)) {\n    throw new Error(`Invalid principal ID: ${String(v)}`);\n  }\n  return v;\n}","typeGuard":"import { Types } from 'mongoose';\nconst isObjectId = (v: unknown): v is string =>\n  typeof v === 'string' && Types.ObjectId.isValid(v);","tryCatchPattern":null,"preventionTips":["For USER/GROUP principals pass the Mongo _id.toString(), not username/email.","Validate with Types.ObjectId.isValid at the API boundary."],"tags":["acl","validation","permissions","mongodb","objectid"],"backgroundTag":null,"analyzedSha":"5ff282f9006c436e561de1afd39a481bea1ef0d8","analyzedAt":"2026-08-12T21:38:08.145Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}