{"record":{"id":"4a838fa2b9fa5caf","repo":"danny-avila/LibreChat","slug":"principal-id-is-required-for-user-group-and-role","errorCode":null,"errorMessage":"Principal ID is required for user, group, and role principals","messagePattern":"Principal ID is required for user, group, and role principals","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"api/server/services/PermissionService.js","lineNumber":81,"sourceCode":" * @param {mongoose.ClientSession} [params.session] - Optional MongoDB session for transactions\n * @returns {Promise<Object>} The created or updated ACL entry\n */\nconst grantPermission = async ({\n  principalType,\n  principalId,\n  resourceType,\n  resourceId,\n  accessRoleId,\n  grantedBy,\n  session,\n}) => {\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      !mongoose.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 || !mongoose.Types.ObjectId.isValid(resourceId)) {","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/danny-avila/LibreChat/blob/5ff282f9006c436e561de1afd39a481bea1ef0d8/api/server/services/PermissionService.js#L63-L99","documentation":"grantPermission() in PermissionService.js:81 throws this when principalType is anything other than PUBLIC and principalId is missing/null. PUBLIC is the only principal that legitimately has no id (it represents everyone); user, group, and role principals must carry an identifier.","triggerScenarios":"A caller sets principalType to USER/GROUP/ROLE but omits principalId or passes null/undefined. Fires after the principalType enum check passes, so it specifically catches the 'right type, no id' mistake.","commonSituations":"UI bug where the selected user/group did not get bound to the request body. A refactor that decoupled principalType from principalId population. Confusing ROLE (id is the role name string) with a missing id.","solutions":["For USER/GROUP, supply a valid ObjectId; for ROLE, supply the role name string.","If you intended 'everyone', use PrincipalType.PUBLIC with no principalId instead.","Validate on the client that a principal is selected before enabling the submit action."],"exampleFix":"// before\ngrantPermission({ principalType: PrincipalType.USER, principalId: null, ... });\n// after\ngrantPermission({ principalType: PrincipalType.USER, principalId: selectedUserId, ... });","handlingStrategy":"validation","validationCode":"function assertPrincipalId(principalType, principalId) {\n  if (principalType !== PrincipalType.PUBLIC && !principalId) {\n    throw new Error(`principalId required for ${principalType}`);\n  }\n}","typeGuard":"const hasRequiredPrincipalId = (type, id) => type === PrincipalType.PUBLIC ? true : !!id;","tryCatchPattern":null,"preventionTips":["Bind the selected principal's id in the same handler that sets principalType.","Disable the submit action until a non-PUBLIC principal has an id."],"tags":["permissions","acl","validation"],"backgroundTag":null,"analyzedSha":"5ff282f9006c436e561de1afd39a481bea1ef0d8","analyzedAt":"2026-08-12T21:38:08.145Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}