danny-avila/LibreChat · error · Error

Group principal not found

Error message

Group principal not found

What it means

ensureLocalGroupPrincipalExists() in PermissionService.js:46 throws when db.findGroupById(principalId) returns null — the principalId supplied for a GROUP principal does not resolve to an existing local group. Mirrors the user principal check; prevents phantom-group ACL entries.

Source

Thrown at api/server/services/PermissionService.js:46

const validateResourceType = (resourceType) => {
  const validTypes = Object.values(ResourceType);
  if (!validTypes.includes(resourceType)) {
    throw new Error(`Invalid resourceType: ${resourceType}. Valid types: ${validTypes.join(', ')}`);
  }
};

const ensureLocalUserPrincipalExists = async (principalId) => {
  const user = await db.findUser({ _id: principalId }, '_id');
  if (!user) {
    throw new Error('User principal not found');
  }
  return user._id.toString();
};

const ensureLocalGroupPrincipalExists = async (principalId) => {
  const group = await db.findGroupById(principalId, { _id: 1 });
  if (!group) {
    throw new Error('Group principal not found');
  }
  return group._id.toString();
};

/**
 * @import { TPrincipal } from 'librechat-data-provider'
 */
/**
 * Grant a permission to a principal for a resource using a role
 * @param {Object} params - Parameters for granting role-based permission
 * @param {string} params.principalType - PrincipalType.USER, PrincipalType.GROUP, or PrincipalType.PUBLIC
 * @param {string|mongoose.Types.ObjectId|null} params.principalId - The ID of the principal (null for PrincipalType.PUBLIC)
 * @param {string} params.resourceType - Type of resource (e.g., 'agent')
 * @param {string|mongoose.Types.ObjectId} params.resourceId - The ID of the resource
 * @param {string} params.accessRoleId - The ID of the role (e.g., AccessRoleIds.AGENT_VIEWER, AccessRoleIds.AGENT_EDITOR)
 * @param {string|mongoose.Types.ObjectId} params.grantedBy - User ID granting the permission
 * @param {mongoose.ClientSession} [params.session] - Optional MongoDB session for transactions
 * @returns {Promise<Object>} The created or updated ACL entry

View on GitHub (pinned to 5ff282f900)

Solutions

  1. Confirm the group still exists (db.findGroupById) before issuing the grant.
  2. If using Entra groups, ensure the Entra-to-local group sync has materialized the group.
  3. Refresh the group list in the UI and reselect if the group was removed.
Defensive patterns

Strategy: validation

Validate before calling

async function assertGroupExists(principalId) {
  const g = await db.findGroupById(principalId, { _id: 1 });
  if (!g) throw new Error(`no group for ${principalId}`);
}

Prevention

When it happens

Trigger: grantPermission is called with principalType=GROUP and a principalId that is a valid ObjectId but no group with that _id exists. Occurs when sharing with a group that was deleted, or when an Entra-synced group id is used but local group sync has not run.

Common situations: Group was deleted between the UI listing it and the grant call. Entra ID groups feature enabled but the group has not yet been mirrored locally. Stale group id cached on the client.

Related errors


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