RocketChat/Rocket.Chat · error · Meteor.Error

error-invalid-integration

error-invalid-integration

Error message

Invalid integration

What it means

Thrown by updateIncomingIntegration when the current integration record cannot be loaded. With 'manage-incoming-integrations' the lookup is Integrations.findOneById(integrationId); with 'manage-own-incoming-integrations' it additionally filters '_createdBy._id': userId. So it fires for a nonexistent id, an already-deleted integration, or an existing integration created by somebody else while the caller holds only the manage-own permission.

Source

Thrown at apps/meteor/server/meteor-methods/integrations/incoming/updateIncomingIntegration.ts:68

	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);
		});
	}

	const isFrozen = isScriptEngineFrozen(scriptEngine);

	// Default to transpiling with Babel for backwards compatibility; integrations

View on GitHub (pinned to b2c16d5842)

Solutions

  1. Re-fetch the integration list (GET /api/v1/integrations.list) and confirm the id still exists before updating
  2. If you hold only manage-own, edit only integrations you created; route other edits through an admin
  3. Treat as 'already removed' and refresh the UI if the record was deleted concurrently

Example fix

// before
await Meteor.callAsync('updateIncomingIntegration', staleId, payload); // -> error-invalid-integration
// after
const fresh = await Meteor.callAsync('listIncomingIntegrations');
const current = fresh.integrations?.find((i) => i._id === integrationId);
if (current) await Meteor.callAsync('updateIncomingIntegration', current._id, { ...payload, channel: current.channel.join(',') });
Defensive patterns

Strategy: validation

Validate before calling

const { integrations } = await Meteor.callAsync('listIncomingIntegrations');
const target = integrations.find((i) => i._id === integrationId);
if (!target) throw new Error('integration missing or not owned by caller');
await Meteor.callAsync('updateIncomingIntegration', integrationId, { ...integration, channel: integration.channel ?? target.channel.join(',') });

Try / catch

try {
  await Meteor.callAsync('updateIncomingIntegration', integrationId, integration);
} catch (err) {
  if (err instanceof Meteor.Error && err.error === 'error-invalid-integration') { /* refresh list; record deleted or owned by someone else */ }
}

Prevention

When it happens

Trigger: Updating with a stale id after the integration was deleted elsewhere; manage-own user editing a teammate's webhook; id copied with a typo or trailing whitespace.

Common situations: Two admins editing concurrently (one deletes, the other saves); integrations list screens holding stale data after a workspace restore; cross-team ownership boundaries.

Related errors


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