calcom/cal.diy · error · BadRequestException

Verification failed

Error message

Verification failed

What it means

The fallback BadRequestException('Verification failed') in verifyEmailCodeUnAuthenticated — fires for ANY upstream error whose message is not 'invalid_code' or 'BAD_REQUEST'. Because those two predicates never match the real upstream strings (see errors 20/21), THIS is the error that actually surfaces for almost every failure: wrong code, expired code, missing email/code, and rate-limit errors all collapse into this generic 400. The original cause is swallowed, which makes debugging hard.

Source

Thrown at apps/api/v2/src/modules/atoms/services/verification-atom.service.ts:40

  ) {}

  async checkEmailVerificationRequired(input: CheckEmailVerificationRequiredParams) {
    return await checkEmailVerificationRequired(input);
  }

  async verifyEmailCodeUnAuthenticated(input: VerifyEmailCodeInput) {
    try {
      return await verifyCodeUnAuthenticated(input.email, input.code);
    } catch (error) {
      if (error instanceof Error) {
        if (error.message === "invalid_code") {
          throw new BadRequestException("Invalid verification code");
        }
        if (error.message === "BAD_REQUEST") {
          throw new BadRequestException("Email and code are required");
        }
      }
      throw new BadRequestException("Verification failed");
    }
  }

  async verifyEmailCodeAuthenticated(user: UserWithProfile, input: VerifyEmailCodeInput) {
    try {
      return await verifyCodeAuthenticated({
        user,
        email: input.email,
        code: input.code,
      });
    } catch (error) {
      if (error instanceof Error) {
        if (error.message === "invalid_code") {
          throw new BadRequestException("Invalid verification code");
        }
        if (error.message === "BAD_REQUEST") {
          throw new BadRequestException("Email, code, and user ID are required");
        }

View on GitHub (pinned to 176037d0af)

Solutions

  1. Re-request a verification code and submit it immediately within the 15-minute TOTP window.
  2. Confirm CALENDSO_ENCRYPTION_KEY is the same on the issuing and verifying service.
  3. If under heavy retry, wait for the rate-limit window to reset (identifier is keyed on the email hash).
  4. As a platform maintainer: widen the catch to preserve the upstream message (or rethrow typed ErrorWithCode) so 'wrong code' vs 'rate limited' vs 'missing input' are distinguishable instead of all mapping to 'Verification failed'.

Example fix

// before
throw new BadRequestException("Verification failed");

// after — preserve the real cause for distinguishable client errors
throw new BadRequestException(error instanceof Error ? error.message : "Verification failed");
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check inputs to avoid the generic fallback
if (!input.email || !input.code) {
  throw new BadRequestException('Email and code are required');
}
// Pre-check the code format to avoid a wasted TOTP attempt
if (!/^\d{4,8}$/.test(input.code)) {
  throw new BadRequestException('Code must be 4-8 digits');
}

Try / catch

try {
  await service.verifyEmailCodeUnAuthenticated(input);
} catch (e) {
  if (e instanceof BadRequestException) {
    // 'Verification failed' — could be wrong code, missing input, OR rate limit.
    // Surface a 're-enter code' UX; offer to resend.
  }
  throw e;
}

Prevention

When it happens

Trigger: Any POST to the unauthenticated verify endpoint whose upstream verifyCodeUnAuthenticated throws — including wrong code (Error 'Invalid verification code'), missing fields (Error 'Email and code are required'), or checkRateLimitAndThrowError rate-limit exhaustion. All resolve to 'Verification failed'.

Common situations: End user types the wrong code; code expired past the 900s TOTP step; too many verify attempts tripped the core rate limiter on identifier emailVerifyCode.<hash>; CALENDSO_ENCRYPTION_KEY mismatch between issuer and verifier; client omitted email or code.

Related errors


AI-assisted analysis of calcom/cal.diy@176037d0af (2026-08-12). Data as JSON: /api/errors/14c586bfd8a38be2. Report an issue: GitHub.