RocketChat/Rocket.Chat · error · Meteor.Error
not_authorized
not_authorized
Error message
Unauthorized
What it means
While resolving the current integration, updateOutgoingIntegration branches on permissions: 'manage-outgoing-integrations' allows findOneById on any integration, 'manage-own-outgoing-integrations' scopes the query to _createdBy._id === userId, and holding neither throws not_authorized 'Unauthorized'. Note this throw happens before the not-found check, so an unauthorized caller cannot distinguish 'no permission' from 'does not exist'.
Source
Thrown at apps/meteor/server/meteor-methods/integrations/outgoing/updateOutgoingIntegration.ts:46
const integration = await validateOutgoingIntegration(_integration, userId);
if (!integration.token || integration.token.trim() === '') {
throw new Meteor.Error('error-invalid-token', 'Invalid token', {
method: 'updateOutgoingIntegration',
});
}
let currentIntegration: IIntegration | null;
if (await hasPermissionAsync(userId, 'manage-outgoing-integrations')) {
currentIntegration = await Integrations.findOneById(integrationId);
} else if (await hasPermissionAsync(userId, 'manage-own-outgoing-integrations')) {
currentIntegration = await Integrations.findOne({
'_id': integrationId,
'_createdBy._id': userId,
});
} else {
throw new Meteor.Error('not_authorized', 'Unauthorized', {
method: 'updateOutgoingIntegration',
});
}
if (!currentIntegration) {
throw new Meteor.Error('invalid_integration', '[methods] updateOutgoingIntegration -> integration not found');
}
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 'manage-outgoing-integrations' or 'manage-own-outgoing-integrations' to the caller's role
- Re-login/refresh so the new permission reaches the session or token context
- Retry with an admin-owned API token for the PUT /v1/integrations.update call
Defensive patterns
Strategy: validation
Validate before calling
const canManageAll = await hasPermissionAsync(uid, 'manage-outgoing-integrations');
const canManageOwn = !canManageAll && (await hasPermissionAsync(uid, 'manage-own-outgoing-integrations'));
if (!canManageAll && !canManageOwn) {
throw new Meteor.Error('not_authorized', 'Missing integration permissions');
} Try / catch
try {
await Meteor.callAsync('updateOutgoingIntegration', id, payload);
} catch (err) {
if (err instanceof Meteor.Error && err.error === 'not_authorized') {
// request one of the two manage permissions; do not retry unchanged
return;
}
throw err;
} Prevention
- Gate outgoing-integration edit UI on the manage permissions
- Remember manage-own only reaches integrations you created
- Refresh sessions and tokens after permission changes
When it happens
Trigger: Calling updateOutgoingIntegration (DDP or PUT /v1/integrations.update) with a user whose roles lack both integration permissions; permissions revoked after the admin UI was loaded.
Common situations: Non-admin users editing outgoing webhooks without manage-own-outgoing-integrations; stale sessions after role changes; API tokens belonging to under-privileged users.
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
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/07f018e58aab3591.
Report an issue: GitHub.