{"record":{"id":"cc1766ac23405f47","repo":"danny-avila/LibreChat","slug":"invalid-resource-id-resourceid","errorCode":null,"errorMessage":"Invalid resource ID: ${resourceId}","messagePattern":"Invalid resource ID: (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"api/server/services/PermissionService.js","lineNumber":100,"sourceCode":"    }\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)) {\n      throw new Error(`Invalid resource ID: ${resourceId}`);\n    }\n\n    validateResourceType(resourceType);\n\n    // Get the role to determine permission bits\n    const role = await db.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}`,\n      );\n    }\n    return await db.grantPermission(\n      principalType,","sourceCodeStart":82,"sourceCodeEnd":118,"githubUrl":"https://github.com/danny-avila/LibreChat/blob/5ff282f9006c436e561de1afd39a481bea1ef0d8/api/server/services/PermissionService.js#L82-L118","documentation":"Thrown by PermissionService.grantPermission when the resourceId argument is either falsy or fails mongoose.Types.ObjectId.isValid. Grant operations require a concrete MongoDB ObjectId identifying the target resource (agent, prompt, etc.), so any non-24-hex value is rejected before the ACL entry is written. The check is a hard precondition before validateResourceType and the role lookup run.","triggerScenarios":"Calling grantPermission({ resourceType, resourceId, ... }) with resourceId set to undefined, null, '', a slug, a numeric id, or a truncated/malformed hex string. Also hit when a caller passes req.params.id from a route whose value was never validated, or when a resource was created but its _id was not propagated to the grant call.","commonSituations":"Frontend sending an unsaved/temporary id before the resource document exists; copy-paste of an id with a trailing newline; passing the principalId into the resourceId slot by mistake; integration tests using arbitrary strings like 'test-agent'.","solutions":["Confirm resourceId is the resource document's _id (a 24-char hex string from MongoDB) and is defined before calling grantPermission.","Validate the id upstream (Joi/Zod route schema with .hex().length(24) or mongoose.isValidObjectId) so the call never reaches PermissionService with bad input.","Check the call site argument order against the destructured signature { principalType, principalId, resourceType, resourceId, accessRoleId, grantedBy, session } to rule out swapped arguments.","Log resourceId at the caller to catch undefined propagation from a missing DB insert result."],"exampleFix":"// before\nawait grantPermission({ resourceType: 'agent', resourceId: agent.slug, principalType: PrincipalType.USER, principalId, accessRoleId, grantedBy });\n\n// after\nconst resourceId = agent._id?.toString();\nif (!mongoose.isValidObjectId(resourceId)) {\n  throw new Error(`Cannot grant: agent has no valid _id (got ${agent.slug})`);\n}\nawait grantPermission({ resourceType: 'agent', resourceId, principalType: PrincipalType.USER, principalId, accessRoleId, grantedBy });","handlingStrategy":"validation","validationCode":"const mongoose = require('mongoose');\nfunction assertValidResourceId(resourceId) {\n  if (!resourceId || !mongoose.Types.ObjectId.isValid(resourceId)) {\n    throw new Error(`Invalid resource ID: ${resourceId}`);\n  }\n}\n// call before grantPermission:\nassertValidResourceId(resourceId);","typeGuard":"const mongoose = require('mongoose');\nconst isResourceId = (id) => typeof id === 'string' && /^[0-9a-fA-F]{24}$/.test(id) && mongoose.Types.ObjectId.isValid(id);","tryCatchPattern":"try {\n  await grantPermission({ ..., resourceId });\n} catch (err) {\n  if (err.message.startsWith('Invalid resource ID')) return res.status(400).json({ message: err.message });\n  throw err;\n}","preventionTips":["Validate all id params at the route boundary with a hex(24) schema.","Always derive resourceId from a document's _id, never from a slug or user input.","Write integration tests that pass a non-ObjectId to assert the 400 path."],"tags":["permissions","validation","mongodb","resource-id"],"backgroundTag":null,"analyzedSha":"5ff282f9006c436e561de1afd39a481bea1ef0d8","analyzedAt":"2026-08-12T21:38:08.145Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}