danny-avila/LibreChat · error · Error
Invalid resource ID: ${resourceId}
Error message
Invalid resource ID: ${resourceId} What it means
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.
Source
Thrown at api/server/services/PermissionService.js:100
}
// Validate principalId based on type
if (principalId && principalType === PrincipalType.ROLE) {
// Role IDs are strings (role names)
if (typeof principalId !== 'string' || principalId.trim().length === 0) {
throw new Error(`Invalid role ID: ${principalId}`);
}
} else if (
principalType &&
principalType !== PrincipalType.PUBLIC &&
!mongoose.Types.ObjectId.isValid(principalId)
) {
// User and Group IDs must be valid ObjectIds
throw new Error(`Invalid principal ID: ${principalId}`);
}
if (!resourceId || !mongoose.Types.ObjectId.isValid(resourceId)) {
throw new Error(`Invalid resource ID: ${resourceId}`);
}
validateResourceType(resourceType);
// Get the role to determine permission bits
const role = await db.findRoleByIdentifier(accessRoleId);
if (!role) {
throw new Error(`Role ${accessRoleId} not found`);
}
// Ensure the role is for the correct resource type
if (role.resourceType !== resourceType) {
throw new Error(
`Role ${accessRoleId} is for ${role.resourceType} resources, not ${resourceType}`,
);
}
return await db.grantPermission(
principalType,View on GitHub (pinned to 5ff282f900)
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.
Example fix
// before
await grantPermission({ resourceType: 'agent', resourceId: agent.slug, principalType: PrincipalType.USER, principalId, accessRoleId, grantedBy });
// after
const resourceId = agent._id?.toString();
if (!mongoose.isValidObjectId(resourceId)) {
throw new Error(`Cannot grant: agent has no valid _id (got ${agent.slug})`);
}
await grantPermission({ resourceType: 'agent', resourceId, principalType: PrincipalType.USER, principalId, accessRoleId, grantedBy }); Defensive patterns
Strategy: validation
Validate before calling
const mongoose = require('mongoose');
function assertValidResourceId(resourceId) {
if (!resourceId || !mongoose.Types.ObjectId.isValid(resourceId)) {
throw new Error(`Invalid resource ID: ${resourceId}`);
}
}
// call before grantPermission:
assertValidResourceId(resourceId); Type guard
const mongoose = require('mongoose');
const isResourceId = (id) => typeof id === 'string' && /^[0-9a-fA-F]{24}$/.test(id) && mongoose.Types.ObjectId.isValid(id); Try / catch
try {
await grantPermission({ ..., resourceId });
} catch (err) {
if (err.message.startsWith('Invalid resource ID')) return res.status(400).json({ message: err.message });
throw err;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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'.
Related errors
- Invalid principal ID: ${principalId}
- Invalid principal ID: ${principalId}
- Invalid resource ID: ${resourceId}
- Invalid resourceType: ${resourceType}. Valid types: ${validT
- User principal not found
AI-assisted analysis of danny-avila/LibreChat@5ff282f900 (2026-08-12).
Data as JSON: /api/errors/cc1766ac23405f47.
Report an issue: GitHub.