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

  1. Confirm resourceId is the resource document's _id (a 24-char hex string from MongoDB) and is defined before calling grantPermission.
  2. Validate the id upstream (Joi/Zod route schema with .hex().length(24) or mongoose.isValidObjectId) so the call never reaches PermissionService with bad input.
  3. Check the call site argument order against the destructured signature { principalType, principalId, resourceType, resourceId, accessRoleId, grantedBy, session } to rule out swapped arguments.
  4. 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

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


AI-assisted analysis of danny-avila/LibreChat@5ff282f900 (2026-08-12). Data as JSON: /api/errors/cc1766ac23405f47. Report an issue: GitHub.