n8n-io/n8n · warning · BadRequestError

400

400

Error message

Current password is required to change email

What it means

A BadRequestError (HTTP 400) from validateChangingUserEmail when the user is MFA-disabled (mfaEnabled false), has a password set, but providedCurrentPassword is missing or not a string. To change email without 2FA, the user must re-confirm with their current password; omitting it is rejected.

Source

Thrown at packages/cli/src/controllers/me.controller.ts:159

			if (!payload.mfaCode) {
				throw new BadRequestError('Two-factor code is required to change email');
			}

			const isMfaCodeValid = await this.mfaService.validateMfa(userId, payload.mfaCode, undefined);
			if (!isMfaCodeValid) {
				throw new InvalidMfaCodeError();
			}
		} else {
			if (currentUser.password === null) {
				this.logger.debug('User with no password changed their email', {
					userId: currentUser.id,
					payload: payloadWithoutPassword,
				});
				return;
			}

			if (!providedCurrentPassword || typeof providedCurrentPassword !== 'string') {
				throw new BadRequestError('Current password is required to change email');
			}

			const isProvidedPasswordCorrect = await this.passwordUtility.compare(
				providedCurrentPassword,
				currentUser.password,
			);
			if (!isProvidedPasswordCorrect) {
				throw new BadRequestError(
					'Unable to update profile. Please check your credentials and try again.',
				);
			}
		}
	}

	private isUserManagedByEnv(user: User): boolean {
		const { instanceSettingsLoader } = this.globalConfig;
		return (
			instanceSettingsLoader.ownerManagedByEnv &&

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Include a non-empty currentPassword string in the PATCH /me body when changing email without MFA.
  2. Add a 'current password' input to the email-change form for non-MFA users.
  3. Validate typeof currentPassword === 'string' && currentPassword on the client before submit.

Example fix

// before
await api.patch('/me', { email: newEmail });

// after
await api.patch('/me', { email: newEmail, currentPassword: promptPassword() });
Defensive patterns

Strategy: validation

Validate before calling

// Require current password for email change when MFA is off.
if (!me.mfaEnabled && isEmailChange(payload, me.email)) {
  if (typeof payload.currentPassword !== 'string' || payload.currentPassword.length === 0) {
    throw new Error('Current password is required to change email');
  }
}

Type guard

function hasCurrentPassword(p: { currentPassword?: string }): p is { currentPassword: string } {
  return typeof p.currentPassword === 'string' && p.currentPassword.length > 0;
}

Try / catch

try {
  await api.patch('/me', { ...payload, currentPassword });
} catch (e) {
  if (e.response?.status === 400 && /current password is required/i.test(e.response.data.message)) {
    currentPassword = await promptPassword();
    await api.patch('/me', { ...payload, currentPassword });
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: PATCH /me changing email for a password-based (non-MFA) user where currentPassword is omitted, null, undefined, or a non-string type. The branch is reached because mfaEnabled is false and currentUser.password is non-null.

Common situations: Frontend form omits the current-password field for email change; client serializes the field as null; user leaves the field blank; a script that only sends the new email.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/2ef317b72eb91b41. Report an issue: GitHub.