RocketChat/Rocket.Chat · error · Meteor.Error

not_authorized

not_authorized

Error message

Unauthorized

What it means

Thrown by the deleteIncomingIntegration helper when the caller holds neither 'manage-incoming-integrations' nor 'manage-own-incoming-integrations' (or userId is empty, since both checks are gated on !!userId). Either permission alone is sufficient; the error means the caller has neither at the time of the call.

Source

Thrown at apps/meteor/server/meteor-methods/integrations/incoming/deleteIncomingIntegration.ts:22

import { hasPermissionAsync } from '../../../lib/authorization/hasPermission';
import { methodDeprecationLogger } from '../../../lib/deprecationWarningLogger';
import { notifyOnIntegrationChanged } from '../../../lib/notifyListener';

declare module '@rocket.chat/ddp-client' {
	// eslint-disable-next-line @typescript-eslint/naming-convention
	interface ServerMethods {
		deleteIncomingIntegration(integrationId: string): Promise<boolean>;
	}
}

export const deleteIncomingIntegration = async (integrationId: string, userId: string): Promise<void> => {
	const canManageAllIntegrations = !!userId && (await hasPermissionAsync(userId, 'manage-incoming-integrations'));
	const canManageOwnIntegrations =
		!canManageAllIntegrations && !!userId && (await hasPermissionAsync(userId, 'manage-own-incoming-integrations'));

	if (!canManageAllIntegrations && !canManageOwnIntegrations) {
		throw new Meteor.Error('not_authorized', 'Unauthorized', {
			method: 'deleteIncomingIntegration',
		});
	}

	const integration = await Integrations.removeByIdAndCreatedByIfExists({
		_id: integrationId,
		...(canManageOwnIntegrations && { createdBy: userId }),
	});

	if (!integration) {
		throw new Meteor.Error('error-invalid-integration', 'Invalid integration', {
			method: 'deleteIncomingIntegration',
		});
	}

	void notifyOnIntegrationChanged(integration, 'removed');
};

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Grant 'manage-incoming-integrations' (full) or 'manage-own-incoming-integrations' (own only) to a role the caller holds
  2. Have the caller log out and back in so refreshed permissions reach the client, then retry
  3. Or delete the integration from an admin account; for automation use DELETE /api/v1/integrations.remove

Example fix

// before
await Meteor.callAsync('deleteIncomingIntegration', id); // by a user with no integration permission
// after: perform the delete as an admin-managed service
await fetch(`${root}/api/v1/integrations.remove`, { method: 'POST', headers: { 'X-Auth-Token': adminToken, 'X-User-Id': adminUid }, body: JSON.stringify({ integrationId: id, type: 'webhook-incoming' }) });
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await Meteor.callAsync('deleteIncomingIntegration', integrationId);
} catch (err) {
  if (err instanceof Meteor.Error && err.error === 'not_authorized') { /* hide delete affordance; suggest admin or permission grant */ }
}

Prevention

When it happens

Trigger: A regular user without any integration permission calls deleteIncomingIntegration; permissions were revoked but the client session still caches old role state; userId passed as empty string when invoking the exported helper.

Common situations: Admin removed integration rights as cleanup and users' open admin screens kept the delete button enabled; permission changes requiring re-login to take effect in the client's cached role subscription.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18). Data as JSON: /api/errors/7b785ec9b73906b3. Report an issue: GitHub.