{"record":{"id":"8406077033ef1bf3","repo":"danny-avila/LibreChat","slug":"user-principal-not-found","errorCode":null,"errorMessage":"User principal not found","messagePattern":"User principal not found","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"api/server/services/PermissionService.js","lineNumber":38,"sourceCode":"/** @type {boolean|null} */\nlet transactionSupportCache = null;\n\n/**\n * Validates that the resourceType is one of the supported enum values\n * @param {string} resourceType - The resource type to validate\n * @throws {Error} If resourceType is not valid\n */\nconst validateResourceType = (resourceType) => {\n  const validTypes = Object.values(ResourceType);\n  if (!validTypes.includes(resourceType)) {\n    throw new Error(`Invalid resourceType: ${resourceType}. Valid types: ${validTypes.join(', ')}`);\n  }\n};\n\nconst ensureLocalUserPrincipalExists = async (principalId) => {\n  const user = await db.findUser({ _id: principalId }, '_id');\n  if (!user) {\n    throw new Error('User principal not found');\n  }\n  return user._id.toString();\n};\n\nconst ensureLocalGroupPrincipalExists = async (principalId) => {\n  const group = await db.findGroupById(principalId, { _id: 1 });\n  if (!group) {\n    throw new Error('Group principal not found');\n  }\n  return group._id.toString();\n};\n\n/**\n * @import { TPrincipal } from 'librechat-data-provider'\n */\n/**\n * Grant a permission to a principal for a resource using a role\n * @param {Object} params - Parameters for granting role-based permission","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/danny-avila/LibreChat/blob/5ff282f9006c436e561de1afd39a481bea1ef0d8/api/server/services/PermissionService.js#L20-L56","documentation":"ensureLocalUserPrincipalExists() in PermissionService.js:38 throws when db.findUser({_id: principalId}) returns null — i.e. the principalId passed for a USER principal does not resolve to an existing local user. This is the existence check that prevents ACL entries from referencing phantom users.","triggerScenarios":"grantPermission (or a sibling ACL call) is invoked with principalType=USER and a principalId that is a valid ObjectId shape but does not exist in the users collection. Common right after a user was deleted, or when an id is transcribed from another tenant/environment.","commonSituations":"Cross-environment copy of an agent share that references a user id not present locally. A user was soft/hard-deleted but still referenced in a pending share operation. Multi-tenant leak where a principalId from tenant A is used in tenant B.","solutions":["Verify the principalId corresponds to a currently existing user in the target environment.","If the user was deleted, cancel the share/grant operation rather than retrying.","In multi-tenant setups, confirm the principalId belongs to the same tenant before granting."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"async function assertUserExists(principalId) {\n  const u = await db.findUser({ _id: principalId }, '_id');\n  if (!u) throw new Error(`no user for ${principalId}`);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Look up the user immediately before granting, since a stale id may have been deleted.","In multi-tenant deployments, scope the lookup by tenantId."],"tags":["permissions","acl","users","validation"],"backgroundTag":null,"analyzedSha":"5ff282f9006c436e561de1afd39a481bea1ef0d8","analyzedAt":"2026-08-12T21:38:08.145Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}