Hibernate.orgCommunity Documentation

Chapter 4. Interpolating constraint error messages

4.1. Default message interpolation
4.1.1. Special characters
4.1.2. Interpolation with message expressions
4.1.3. Examples
4.2. Custom message interpolation
4.2.1. ResourceBundleLocator

Message interpolation is the process of creating error messages for violated Bean Validation constraints. In this chapter you will learn how such messages are defined and resolved and how you can plug in custom message interpolators in case the default algorithm is not sufficient for your requirements.

Constraint violation messages are retrieved from so called message descriptors. Each constraint defines its default message descriptor using the message attribute. At declaration time, the default descriptor can be overridden with a specific value as shown in Example 4.1, “Specifying a message descriptor using the message attribute”.


If a constraint is violated, its descriptor will be interpolated by the validation engine using the currently configured MessageInterpolator. The interpolated error message can then be retrieved from the resulting constraint violation by calling ConstraintViolation#getMessage().

Message descriptors can contain message parameters as well as message expressions which will be resolved during interpolation. Message parameters are string literals enclosed in {}, while message expressions are string literals enclosed in ${}. The following algorithm is applied during method interpolation:

  1. Resolve any message parameters by using them as key for the resource bundle ValidationMessages. If this bundle contains an entry for a given message parameter, that parameter will be replaced in the message with the corresponding value from the bundle. This step will be executed recursively in case the replaced value again contains message parameters. The resource bundle is expected to be provided by the application developer, e.g. by adding a file named ValidationMessages.properties to the classpath. You can also create localized error messages by providing locale specific variations of this bundle, such as ValidationMessages_en_US.properties. By default, the JVM’s default locale (Locale#getDefault()) will be used when looking up messages in the bundle.
  2. Resolve any message parameters by using them as key for a resource bundle containing the standard error messages for the built-in constraints as defined in Appendix B of the Bean Validation specification. In the case of Hibernate Validator, this bundle is named org.hibernate.validator.ValidationMessages. If this step triggers a replacement, step 1 is executed again, otherwise step 3 is applied.
  3. Resolve any message parameters by replacing them with the value of the constraint annotation member of the same name. This allows to refer to attribute values of the constraint (e.g. Size#min()) in the error message (e.g. "must be at least ${min}").
  4. Resolve any message expressions by evaluating them as expressions of the Unified Expression Language. See Section 4.1.2, “Interpolation with message expressions” to learn more about the usage of Unified EL in error messages.

Tip

You can find the formal definition of the interpolation algorithm in section 5.3.1.1 of the Bean Validation specification.

As of Hibernate Validator 5 (Bean Validation 1.1) it is possible to use the Unified Expression Language (as defined by JSR 341) in constraint violation messages. This allows to define error messages based on conditional logic and also enables advanced formatting options. The validation engine makes the following objects available in the EL context:

  • the attribute values of the constraint mapped to the attribute names
  • the currently validated value (property, bean, method parameter etc.) under the name validatedValue
  • a bean mapped to the name formatter exposing the var-arg method format(String format, Object…​ args) which behaves like java.util.Formatter.format(String format, Object…​ args).

The following section provides several examples for using EL expressions in error messages.

Example 4.2, “Specifying message descriptors” shows how to make use of the different options for specifying message descriptors.


Validating an invalid Car instance yields constraint violations with the messages shown by the assertions in Example 4.3, “Expected error messages”:

  • the @NotNull constraint on the manufacturer field causes the error message "may not be null", as this is the default message defined by the Bean Validation specification and no specific descriptor is given in the message attribute
  • the @Size constraint on the licensePlate field shows the interpolation of message parameters ({min}, {max}) and how to add the validated value to the error message using the EL expression ${validatedValue}
  • the @Min constraint on seatCount demonstrates how use an EL expression with a ternery expression to dynamically chose singular or plural form, depending on an attribute of the constraint ("There must be at least 1 seat" vs. "There must be at least 2 seats")
  • the message for the @DecimalMax constraint on topSpeed shows how to format the validated value using the formatter instance
  • finally, the @DecimalMax constraint on price shows that parameter interpolation has precedence over expression evaluation, causing the $ sign to show up in front of the maximum price

Tip

Only actual constraint attributes can be interpolated using message parameters in the form {attributeName}. When referring to the validated value or custom expression variables added to the interpolation context (see Section 11.9.1, “HibernateConstraintValidatorContext), an EL expression in the form ${attributeName} must be used.


If the default message interpolation algorithm does not fit your requirements it is also possible to plug in a custom MessageInterpolator implementation.

Custom interpolators must implement the interface javax.validation.MessageInterpolator. Note that implementations must be thread-safe. It is recommended that custom message interpolators delegate final implementation to the default interpolator, which can be obtained via Configuration#getDefaultMessageInterpolator().

In order to use a custom message interpolator it must be registered either by configuring it in the Bean Validation XML descriptor META-INF/validation.xml (see Section 7.1, “Configuring the validator factory in validation.xml) or by passing it when bootstrapping a ValidatorFactory or Validator (see Section 8.2.1, “MessageInterpolator and Section 8.3, “Configuring a Validator”, respectively).

In some use cases you want to use the message interpolation algorithm as defined by the Bean Validation specification, but retrieve error messages from other resource bundles than ValidationMessages. In this situation Hibernate Validator’s ResourceBundleLocator SPI can help.

The default message interpolator in Hibernate Validator, ResourceBundleMessageInterpolator, delegates retrieval of resource bundles to that SPI. Using an alternative bundle only requires passing an instance of PlatformResourceBundleLocator with the bundle name when bootstrapping the ValidatorFactory as shown in Example 4.4, “Using a specific resource bundle”.


Of course you also could implement a completely different ResourceBundleLocator, which for instance returns bundles backed by records in a database. In this case you can obtain the default locator via HibernateValidatorConfiguration#getDefaultResourceBundleLocator(), which you e.g. could use as fall-back for your custom locator.

Besides PlatformResourceBundleLocator, Hibernate Validator provides another resource bundle locator implementation out of the box, namely AggregateResourceBundleLocator, which allows to retrieve error messages from more than one resource bundle. You could for instance use this implementation in a multi-module application where you want to have one message bundle per module. Example 4.5, “Using AggregateResourceBundleLocator shows how to use AggregateResourceBundleLocator.


Note that the bundles are processed in the order as passed to the constructor. That means if several bundles contain an entry for a given message key, the value will be taken from the first bundle in the list containing the key.