RocketChat/Rocket.Chat · error · Meteor.Error
not_authorized
not_authorized
Error message
Unauthorized
What it means
Thrown by updateIncomingIntegration when the caller holds neither 'manage-incoming-integrations' nor 'manage-own-incoming-integrations'. With the first, the integration is looked up globally; with the second, only among records created by the caller; with neither, the method aborts with not_authorized before any update.
Source
Thrown at apps/meteor/server/meteor-methods/integrations/incoming/updateIncomingIntegration.ts:62
export const updateIncomingIntegration = async (
userId: string,
integrationId: string,
integration: INewIncomingIntegration | IUpdateIncomingIntegration,
): Promise<IIntegration | null> => {
const channels = validateChannels(integration.channel);
let currentIntegration;
if (await hasPermissionAsync(userId, 'manage-incoming-integrations')) {
currentIntegration = await Integrations.findOneById(integrationId);
} else if (await hasPermissionAsync(userId, 'manage-own-incoming-integrations')) {
currentIntegration = await Integrations.findOne({
'_id': integrationId,
'_createdBy._id': userId,
});
} else {
throw new Meteor.Error('not_authorized', 'Unauthorized', {
method: 'updateIncomingIntegration',
});
}
if (!currentIntegration) {
throw new Meteor.Error('error-invalid-integration', 'Invalid integration', {
method: 'updateIncomingIntegration',
});
}
const oldScriptEngine = currentIntegration.scriptEngine;
const scriptEngine = integration.scriptEngine ?? oldScriptEngine ?? 'isolated-vm';
if (
integration.script?.trim() &&
(scriptEngine !== oldScriptEngine || integration.script?.trim() !== currentIntegration.script?.trim())
) {
wrapExceptions(() => validateScriptEngine(scriptEngine)).catch((e) => {
throw new Meteor.Error(e.message);View on GitHub (pinned to b2c16d5842)
Solutions
- Grant the caller one of the two permissions: 'manage-incoming-integrations' for all records, 'manage-own-incoming-integrations' for their own
- Re-login after permission changes so refreshed roles reach the client, then retry
- For automated flows use POST /api/v1/integrations.update with an authorized admin token
Example fix
// before
await Meteor.callAsync('updateIncomingIntegration', id, payload); // caller lacks both permissions
// after: call through an authorized service account via REST
await fetch(`${root}/api/v1/integrations.update`, { method: 'POST', headers: { 'X-Auth-Token': token, 'X-User-Id': uid }, body: JSON.stringify({ integrationId: id, ...payload }) }); Defensive patterns
Strategy: try-catch
Try / catch
try {
await Meteor.callAsync('updateIncomingIntegration', integrationId, integration);
} catch (err) {
if (err instanceof Meteor.Error && err.error === 'not_authorized') { /* hide edit UI, prompt for permission or admin */ }
} Prevention
- Gate integration edit screens on a client-visible permission check
- Re-login after permission grants
- Use an authorized REST token for automated updates
When it happens
Trigger: A user whose integration permissions were revoked (but whose client UI still shows the edit screen) submits an update; a fresh user account without any integration role attempts an update.
Common situations: Role cleanup after team changes; permission grants not propagated to the client's cached roles until re-login; automation using a service account that was never given integration permissions.
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.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- error-user-lacks-message-impersonate-permission
- not_authorized
- error-invalid-channel
- error-invalid-channel-start-with-chars
- error-invalid-username
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/61d8c9ccfceedd03.
Report an issue: GitHub.