{"record":{"id":"33445cf429908370","repo":"danny-avila/LibreChat","slug":"invalid-resourcetype-resourcetype-valid-types-33445c","errorCode":null,"errorMessage":"Invalid resourceType: ${resourceType}. Valid types: ${validTypes.join(', ')}","messagePattern":"Invalid resourceType: (.+?)\\. Valid types: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"api/server/services/PermissionService.js","lineNumber":31,"sourceCode":"  getUserEntraGroups,\n  getEntraGroupDetailsBatch,\n  getGroupMembers,\n  getGroupOwners,\n} = require('~/server/services/GraphApiService');\nconst db = require('~/models');\n\n/** @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};","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/danny-avila/LibreChat/blob/5ff282f9006c436e561de1afd39a481bea1ef0d8/api/server/services/PermissionService.js#L13-L49","documentation":"validateResourceType() in PermissionService.js:31 throws when resourceType is not one of the ResourceType enum values (agent, promptGroup, mcpServer, remoteAgent, skill, sharedLink). It is the gate used by grantPermission and related ACL writers to prevent persisting permission records against an unsupported resource category.","triggerScenarios":"A caller of grantPermission/related ACL functions passes a resourceType string that is not in the ResourceType enum — e.g. a typo 'agents', a legacy value 'prompt', or a frontend-invented category. Reached after principal validation but the function is also called directly elsewhere.","commonSituations":"Frontend hard-coded a singular/plural form that does not match the enum. A new resource type was added to the UI but not to the ResourceType enum in data-provider. A migration left stale resourceType strings in older API callers.","solutions":["Pass one of: agent, promptGroup, mcpServer, remoteAgent, skill, sharedLink — exactly as exported by ResourceType.","If you need a new resource category, add it to the ResourceType enum in packages/data-provider first, then use it.","If invoking from TypeScript, type the parameter as ResourceType so the compiler catches typos."],"exampleFix":"// before\ngrantPermission({ resourceType: 'agents', ... });\n// after\nimport { ResourceType } from 'librechat-data-provider';\ngrantPermission({ resourceType: ResourceType.AGENT, ... });","handlingStrategy":"type-guard","validationCode":"const VALID_RESOURCE_TYPES = new Set(Object.values(ResourceType));\nfunction assertResourceType(t) {\n  if (!VALID_RESOURCE_TYPES.has(t)) throw new Error(`unsupported resourceType: ${t}`);\n}","typeGuard":"const isResourceType = (t) => Object.values(ResourceType).includes(t);","tryCatchPattern":null,"preventionTips":["Type the parameter as ResourceType in TS so invalid values fail to compile.","Centralize resourceType constants in one shared module imported by both client and server."],"tags":["permissions","acl","validation","enums"],"backgroundTag":null,"analyzedSha":"5ff282f9006c436e561de1afd39a481bea1ef0d8","analyzedAt":"2026-08-12T21:38:08.145Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}