halo-dev/halo · error · ServerWebInputException

validation.error.email.pattern

validation.error.email.pattern

Error message

validation.error.email.pattern

What it means

Thrown as a ServerWebInputException (HTTP 400) with code 'validation.error.email.pattern' by UserEndpoint.sendEmailVerificationCode. The EmailVerifyRequest body is run through bean validation; if the email field fails its @Email/@Pattern constraint, the message 'validation.error.email.pattern' is thrown. The string is intentionally an i18n code, not human text, so the frontend can localize it.

Source

Thrown at application/src/main/java/run/halo/app/core/endpoint/console/UserEndpoint.java:324

    /**
     * Payload for verifying an email address by code.
     *
     * @param password current password of the authenticated user
     * @param code email verification code
     */
    public record VerifyCodeRequest(
            @Schema(requiredMode = REQUIRED) String password,

            @Schema(requiredMode = REQUIRED, minLength = 1) String code) {}

    private Mono<ServerResponse> sendEmailVerificationCode(ServerRequest request) {
        var emailMono = request.bodyToMono(EmailVerifyRequest.class)
                .switchIfEmpty(Mono.error(() -> new ServerWebInputException("Request body is required.")))
                .doOnNext(emailReq -> {
                    var bindingResult = ValidationUtils.validate(emailReq, validator, request.exchange());
                    if (bindingResult.hasErrors()) {
                        // only email field is validated
                        throw new ServerWebInputException("validation.error.email.pattern");
                    }
                })
                .map(EmailVerifyRequest::email)
                .map(String::toLowerCase);
        return Mono.zip(emailMono, getAuthenticatedUserName())
                .flatMap(tuple -> {
                    var email = tuple.getT1();
                    var username = tuple.getT2();
                    return Mono.just(username)
                            .transformDeferred(sendEmailVerificationCodeRateLimiter(username))
                            .flatMap(u -> emailVerificationService.sendVerificationCode(username, email))
                            .onErrorMap(RequestNotPermitted.class, RateLimitExceededException::new);
                })
                .then(ServerResponse.ok().build());
    }

    <T> RateLimiterOperator<T> verificationEmailRateLimiter(String username) {
        String rateLimiterKey = "verify-email-" + username;

View on GitHub (pinned to d2f5165f9c)

Solutions

  1. Validate the email client-side (RFC-ish regex or HTML5 type=email) before posting the request.
  2. Send a well-formed email string in the JSON body 'email' field, e.g. 'user@example.com'.
  3. Map the response code 'validation.error.email.pattern' to a localized user-facing message in the UI.

Example fix

// before
//   { "email": "foo@@bar" }
// after
//   { "email": "user@example.com" }
Defensive patterns

Strategy: validation

Validate before calling

// validate email before calling sendEmailVerificationCode
private static final Pattern EMAIL =
    Pattern.compile("^[^@\s]+@[^@\s]+\.[^@\s]+$");
if (!EMAIL.matcher(email).matches()) {
    showUserError("Enter a valid email address");
    return;
}

Prevention

When it happens

Trigger: POST /apis/api.console.halo.run/v1alpha1/users/-/send-email-verification-code with a JSON body whose 'email' is missing, empty, or malformed (e.g. 'foo@@bar', 'not-an-email', 'user@'). ValidationUtils.validate reports errors on the email field.

Common situations: Frontend validation bypassed; user typed an invalid email; a migration/import script sends raw unvalidated emails; testing with placeholder strings like 'test' or 'N/A'.

Related errors


AI-assisted analysis of halo-dev/halo@d2f5165f9c (2026-08-14). Data as JSON: /api/errors/d80f821130ac60c6. Report an issue: GitHub.