RocketChat/Rocket.Chat · error · Meteor.Error
error-invalid-token
error-invalid-token
Error message
Invalid token
What it means
updateOutgoingIntegration first normalizes the payload through validateOutgoingIntegration, which spreads the caller's fields without generating a token; if the result has no token (or only whitespace), the update is rejected with error-invalid-token before any lookup or permission branch runs. The same path serves both the DDP method and REST PUT /v1/integrations.update, which forwards bodyParams verbatim — so both require a non-empty token in the update payload.
Source
Thrown at apps/meteor/server/meteor-methods/integrations/outgoing/updateOutgoingIntegration.ts:31
declare module '@rocket.chat/ddp-client' {
// eslint-disable-next-line @typescript-eslint/naming-convention
interface ServerMethods {
updateOutgoingIntegration(
integrationId: string,
integration: INewOutgoingIntegration | IUpdateOutgoingIntegration,
): IIntegration | null;
}
}
export const updateOutgoingIntegration = async (
userId: string,
integrationId: string,
_integration: INewOutgoingIntegration | IUpdateOutgoingIntegration,
): Promise<IIntegration | null> => {
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',
});
}View on GitHub (pinned to b2c16d5842)
Solutions
- Include a non-empty token in the update payload — reuse the integration's existing token or generate a new one to rotate it
- Fetch the current token first (GET /v1/integrations.list) if you want to preserve it
- Rotate deliberately when needed: sending a fresh token string updates the secret used to authenticate outgoing-webhook handshakes
Example fix
// before
await Meteor.callAsync('updateOutgoingIntegration', integrationId, {
name: 'new-name',
channel: '#general', // token omitted -> error-invalid-token
});
// after
await Meteor.callAsync('updateOutgoingIntegration', integrationId, {
name: 'new-name',
channel: '#general',
token: existingIntegration.token, // or a freshly generated value to rotate
}); Defensive patterns
Strategy: validation
Validate before calling
// token is mandatory on every update payload
if (!payload.token || payload.token.trim() === '') {
payload.token = existingIntegration.token ?? generateToken();
} Type guard
const hasValidToken = (p: {
token?: string;
}): p is { token: string } => typeof p.token === 'string' && p.token.trim() !== ''; Try / catch
try {
await Meteor.callAsync('updateOutgoingIntegration', id, payload);
} catch (err) {
if (err instanceof Meteor.Error && err.error === 'error-invalid-token') {
// fill in a non-empty token (existing or rotated) and retry once
return;
}
throw err;
} Prevention
- Always send the token field when updating outgoing integrations — the API does not inherit the stored one
- Fetch the current token from integrations.list before partial updates
- Treat token rotation as intentional: consumers of the webhook must be updated in lockstep
When it happens
Trigger: Sending an update payload that omits the token field or sends token: '' — e.g. a partial-update client assuming unspecified fields keep their stored values.
Common situations: Scripts ported from the UI that only send changed fields; API clients modeled on incoming-integration updates where the token is optional.
Understand the failure class
Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- username-required
- history-data-must-be-defined
- error-invalid-event-type
- error-invalid-username
- error-invalid-targetRoom
AI-assisted analysis of RocketChat/Rocket.Chat@b2c16d5842 (2026-08-18).
Data as JSON: /api/errors/c423a78e8fcba902.
Report an issue: GitHub.