Repository: quarkusio/quarkus
Issue: quarkus-hibernate-validator HV000149: http 500 error caused by IllegalArgumentException if accept-language is invalid

### Describe the bug

Here is a simple endpoint with accept-language header checked with hibernate validator. Header is mandatory and checked with a pattern regex

```java
    @GET
    @Produces({ "application/json;charset=utf-8" })
    public Response hello(@HeaderParam("Accept-Language") @NotNull @Pattern(regexp = "[a-z]{2}") String acceptLanguage) {
        return Response.accepted().build();
    }
```

application.properties set a default lang for validation

```properties
quarkus.default-locale=en
```

if acceptLanguage is omit, validation works and http status 400 response is return with appropriate body
```json
{"exception":null,"propertyViolations":[],"classViolations":[],"parameterViolations":[{"constraintType":"PARAMETER","path":"hello.acceptLanguage","message":"must not be null","value":""}],"returnValueViolations":[]}
```

if acceptLanguage is invalid (don't respect regexp), for bad example value "X77". Http response status is 500 with this stacktrace.

```
023-05-05 13:57:27,805 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (executor-thread-1) HTTP Request to /hello failed, error id: 710c999c-92b3-45e6-81cc-a3ca52ff3010-1: jakarta.validation.ValidationException: HV000149: An exception occurred during message interpolation
	at org.hibernate.validator.internal.engine.validationcontext.AbstractValidationContext.interpolate(AbstractValidationContext.java:336)
	at org.hibernate.validator.internal.engine.validationcontext.AbstractValidationContext.addConstraintFailure(AbstractValidationContext.java:236)
	at org.hibernate.validator.internal.engine.validationcontext.ParameterExecutableValidationContext.addConstraintFailure(ParameterExecutableValidationContext.java:38)
	at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:79)
	at org.hibernate.validator.internal.metadata.core.MetaConstraint.doValidateConstraint(MetaConstraint.java:130)
	at org.hibernate.validator.internal.metadata.core.MetaConstraint.validateConstraint(MetaConstraint.java:123)
	at org.hibernate.validator.internal.engine.ValidatorImpl.validateMetaConstraint(ValidatorImpl.java:555)
	at org.hibernate.validator.internal.engine.ValidatorImpl.validateMetaConstraints(ValidatorImpl.java:537)
	at org.hibernate.validator.internal.engine.ValidatorImpl.validateParametersForSingleGroup(ValidatorImpl.java:991)
	at org.hibernate.validator.internal.engine.ValidatorImpl.validateParametersForGroup(ValidatorImpl.java:932)
	at org.hibernate.validator.internal.engine.ValidatorImpl.validateParametersInContext(ValidatorImpl.java:863)
	at org.hibernate.validator.internal.engine.ValidatorImpl.validateParameters(ValidatorImpl.java:283)
	at org.hibernate.validator.internal.engine.ValidatorImpl.validateParameters(ValidatorImpl.java:235)
	at io.quarkus.hibernate.validator.runtime.interceptor.AbstractMethodValidationInterceptor.validateMethodInvocation(AbstractMethodValidationInterceptor.java:63)
	at io.quarkus.hibernate.validator.runtime.jaxrs.JaxrsEndPointValidationInterceptor.validateMethodInvocation(JaxrsEndPointValidationInterceptor.java:38)
	at io.quarkus.hibernate.validator.runtime.jaxrs.JaxrsEndPointValidationInterceptor_Bean.intercept(Unknown Source)
	at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:42)
	at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:38)
	at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:26)
	at com.example.ExampleResource_Subclass.hello(Unknown Source)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:170)
	at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:130)
	at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:660)
	at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:524)
	at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:474)
	at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
	at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:476)
	at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:434)
	at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:408)
	at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:69)
	at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:492)
	at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:261)
	at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:161)
	at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
	at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:164)
	at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:247)
	at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:82)
	at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:147)
	at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler$1.run(VertxRequestHandler.java:93)
	at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:576)
	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538)
	at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
	at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.IllegalArgumentException: range=x77
	at java.base/java.util.Locale$LanguageRange.<init>(Locale.java:3099)
	at java.base/sun.util.locale.LocaleMatcher.parse(LocaleMatcher.java:525)
	at java.base/java.util.Locale$LanguageRange.parse(Locale.java:3214)
	at io.quarkus.hibernate.validator.runtime.locale.AbstractLocaleResolver.getAcceptableLanguages(AbstractLocaleResolver.java:35)
	at io.quarkus.hibernate.validator.runtime.locale.AbstractLocaleResolver.resolve(AbstractLocaleResolver.java:17)
	at io.quarkus.hibernate.validator.runtime.locale.ResteasyClassicLocaleResolver.resolve(ResteasyClassicLocaleResolver.java:11)
	at io.quarkus.hibernate.validator.runtime.locale.LocaleResolversWrapper.resolve(LocaleResolversWrapper.java:27)
	at org.hibernate.validator.messageinterpolation.AbstractMessageInterpolator.interpolate(AbstractMessageInterpolator.java:343)
	at org.hibernate.validator.internal.engine.validationcontext.AbstractValidationContext.interpolate(AbstractValidationContext.java:327)
	... 49 more
```

### Expected behavior

If accept-language is not usable, fall back to default lang defined in application.properties.

Expected behavior is to have a 400 http status with a body which indicate an error on hello.acceptLanguage field with message `must match [a-z]{2}`


### Actual behavior

_No response_

### How to Reproduce?

I've created a minimal maven demo project to show the problem.

see ExampleResourceTest.testHelloEndpointInvalidAcceptLanguage unit test

[demo.zip](https://github.com/quarkusio/quarkus/files/11406025/demo.zip)


### Output of `uname -a` or `ver`

Linux xtream-pc 6.1.25-1-MANJARO #1 SMP PREEMPT_DYNAMIC Thu Apr 20 13:48:36 UTC 2023 x86_64 GNU/Linux

### Output of `java -version`

openjdk version "17.0.6" 2023-01-17

### GraalVM version (if different from Java)

_No response_

### Quarkus version or git rev

3.0.2.Final

### Build tool (ie. output of `mvnw --version` or `gradlew --version`)

Apache Maven 3.8.7 (b89d5959fcde851dcb1c8946a785a163f14e1e29)

### Additional information

_No response_

Changed files:
extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLLocaleResolver.java
extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/locale/AbstractLocaleResolver.java
integration-tests/hibernate-validator/src/test/java/io/quarkus/it/hibernate/validator/HibernateValidatorFunctionalityTest.java

diff --git a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/locale/AbstractLocaleResolver.java b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/locale/AbstractLocaleResolver.java
index 9e541214089..2c5674b07e8 100644
--- a/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/locale/AbstractLocaleResolver.java
+++ b/extensions/hibernate-validator/runtime/src/main/java/io/quarkus/hibernate/validator/runtime/locale/AbstractLocaleResolver.java
@@ -7,20 +7,23 @@
 
 import org.hibernate.validator.spi.messageinterpolation.LocaleResolver;
 import org.hibernate.validator.spi.messageinterpolation.LocaleResolverContext;
+import org.jboss.logging.Logger;
 
 abstract class AbstractLocaleResolver implements LocaleResolver {
 
+    private static final Logger log = Logger.getLogger(AbstractLocaleResolver.class);
+    private static final String ACCEPT_HEADER = "Accept-Language";
+
     protected abstract Map<String, List<String>> getHeaders();
 
     @Override
     public Locale resolve(LocaleResolverContext context) {
         Optional<List<Locale.LanguageRange>> localePriorities = getAcceptableLanguages();
-        if (!localePriorities.isPresent()) {
+        if (localePriorities.isEmpty()) {
             return null;
         }
-
         List<Locale> resolvedLocales = Locale.filter(localePriorities.get(), context.getSupportedLocales());
-        if (resolvedLocales.size() > 0) {
+        if (!resolvedLocales.isEmpty()) {
             return resolvedLocales.get(0);
         }
 
@@ -30,9 +33,17 @@ public Locale resolve(LocaleResolverContext context) {
     private Optional<List<Locale.LanguageRange>> getAcceptableLanguages() {
         Map<String, List<String>> httpHeaders = getHeaders();
         if (httpHeaders != null) {
-            List<String> acceptLanguageList = httpHeaders.get("Accept-Language");
+            List<String> acceptLanguageList = httpHeaders.get(ACCEPT_HEADER);
             if (acceptLanguageList != null && !acceptLanguageList.isEmpty()) {
-                return Optional.of(Locale.LanguageRange.parse(acceptLanguageList.get(0)));
+                try {
+                    return Optional.of(Locale.LanguageRange.parse(acceptLanguageList.get(0)));
+                } catch (IllegalArgumentException e) {
+                    // this can happen when parsing malformed locale range string
+                    if (log.isDebugEnabled()) {
+                        log.debug("Unable to parse the \\"Accept-Language\\" header. \\"" + acceptLanguageList.get(0)
+                                + "\\" is not a valid language range string.", e);
+                    }
+                }
             }
         }
 
diff --git a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLLocaleResolver.java b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLLocaleResolver.java
index f4a1f658dc0..d34dc2d8678 100644
--- a/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLLocaleResolver.java
+++ b/extensions/smallrye-graphql/runtime/src/main/java/io/quarkus/smallrye/graphql/runtime/SmallRyeGraphQLLocaleResolver.java
@@ -9,6 +9,7 @@
 
 import org.hibernate.validator.spi.messageinterpolation.LocaleResolver;
 import org.hibernate.validator.spi.messageinterpolation.LocaleResolverContext;
+import org.jboss.logging.Logger;
 
 import graphql.schema.DataFetchingEnvironment;
 import io.smallrye.graphql.execution.context.SmallRyeContext;
@@ -20,12 +21,13 @@
 @Singleton
 public class SmallRyeGraphQLLocaleResolver implements LocaleResolver {
 
+    private static final Logger log = Logger.getLogger(SmallRyeGraphQLLocaleResolver.class);
     private static final String ACCEPT_HEADER = "Accept-Language";
 
     @Override
     public Locale resolve(LocaleResolverContext context) {
         Optional<List<Locale.LanguageRange>> localePriorities = getAcceptableLanguages();
-        if (!localePriorities.isPresent()) {
+        if (localePriorities.isEmpty()) {
             return null;
         }
         List<Locale> resolvedLocales = Locale.filter(localePriorities.get(), context.getSupportedLocales());
@@ -41,7 +43,15 @@ private Optional<List<Locale.LanguageRange>> getAcceptableLanguages() {
         if (httpHeaders != null) {
             List<String> acceptLanguageList = httpHeaders.get(ACCEPT_HEADER);
             if (acceptLanguageList != null && !acceptLanguageList.isEmpty()) {
-                return Optional.of(Locale.LanguageRange.parse(acceptLanguageList.get(0)));
+                try {
+                    return Optional.of(Locale.LanguageRange.parse(acceptLanguageList.get(0)));
+                } catch (IllegalArgumentException e) {
+                    // this can happen when parsing malformed locale range string
+                    if (log.isDebugEnabled()) {
+                        log.debug("Unable to parse the \\"Accept-Language\\" header. \\"" + acceptLanguageList.get(0)
+                                + "\\" is not a valid language range string.", e);
+                    }
+                }
             }
         }
         return Optional.empty();
diff --git a/integration-tests/hibernate-validator/src/test/java/io/quarkus/it/hibernate/validator/HibernateValidatorFunctionalityTest.java b/integration-tests/hibernate-validator/src/test/java/io/quarkus/it/hibernate/validator/HibernateValidatorFunctionalityTest.java
index e8ffd244f84..880841201ec 100644
--- a/integration-tests/hibernate-validator/src/test/java/io/quarkus/it/hibernate/validator/HibernateValidatorFunctionalityTest.java
+++ b/integration-tests/hibernate-validator/src/test/java/io/quarkus/it/hibernate/validator/HibernateValidatorFunctionalityTest.java
@@ -367,6 +367,16 @@ public void testValidationMessageLocale() {
                 .body(containsString("Vrijednost ne zadovoljava uzorak"));
     }
 
+    @Test
+    public void testValidationMessageInvalidAcceptLanguageHeaderLeadsToDefaultLocaleBeingUsed() {
+        RestAssured.given()
+                .header("Accept-Language", "invalid string")
+                .when()
+                .get("/hibernate-validator/test/test-validation-message-locale/1")
+                .then()
+                .body(containsString("Value is not in line with the pattern"));
+    }
+
     @Test
     public void testValidationMessageDefaultLocale() {
         RestAssured.given()