Affects: Spring Framework 6.0.3


I put the @Validated on the controller, when i call any action on that controller, it fail with flowing exception

java.lang.ArrayIndexOutOfBoundsException: Index 9 out of bounds for length 9
        at java.base/java.util.Arrays$ArrayList.get(Arrays.java:4165)
        Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
        *__checkpoint ⇢ com.nxin.passport.application.support.filter.FormDataParserFilter [DefaultWebFilterChain]
        *__checkpoint ⇢ com.nxin.passport.application.support.filter.UrlRewriteFilter [DefaultWebFilterChain]
        *__checkpoint ⇢ org.jasig.cas.client.webflux.authentication.AssertionFilter [DefaultWebFilterChain]
        *__checkpoint ⇢ org.jasig.cas.client.webflux.authentication.AuthenticationFilter [DefaultWebFilterChain]
        *__checkpoint ⇢ org.jasig.cas.client.webflux.authentication.SingleSignOutFilter [DefaultWebFilterChain]
        *__checkpoint ⇢ HTTP POST "/sec/user-info" [ExceptionHandlingWebHandler]
Original Stack Trace:
                at java.base/java.util.Arrays$ArrayList.get(Arrays.java:4165)
                at org.hibernate.validator.internal.properties.javabean.JavaBeanExecutable.getParameterName(JavaBeanExecutable.java:86)
                at org.hibernate.validator.internal.metadata.aggregated.ParameterMetaData$Builder.build(ParameterMetaData.java:165)
                at org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData$Builder.findParameterMetaData(ExecutableMetaData.java:436)
                at org.hibernate.validator.internal.metadata.aggregated.ExecutableMetaData$Builder.build(ExecutableMetaData.java:391)
                at org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataBuilder$BuilderDelegate.build(BeanMetaDataBuilder.java:260)
                at org.hibernate.validator.internal.metadata.aggregated.BeanMetaDataBuilder.build(BeanMetaDataBuilder.java:133)
                at org.hibernate.validator.internal.metadata.BeanMetaDataManagerImpl.createBeanMetaData(BeanMetaDataManagerImpl.java:206)
                at org.hibernate.validator.internal.metadata.BeanMetaDataManagerImpl.getBeanMetaData(BeanMetaDataManagerImpl.java:165)
                at org.hibernate.validator.internal.engine.ValidatorImpl.validateParameters(ValidatorImpl.java:267)
                at org.hibernate.validator.internal.engine.ValidatorImpl.validateParameters(ValidatorImpl.java:235)
                at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:121)
                at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
                at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:752)
                at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:703)
                at com.nxin.passport.account.controllers.SecController$$SpringCGLIB$$0.getUserInfo(<generated>)
                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.springframework.web.reactive.result.method.InvocableHandlerMethod.lambda$invoke$0(InvocableHandlerMethod.java:145)
                at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:132)
                at reactor.core.publisher.MonoZip$ZipCoordinator.signal(MonoZip.java:293)
                at reactor.core.publisher.MonoZip$ZipInner.onNext(MonoZip.java:474)
                at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:180)
                at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onNext(FluxDefaultIfEmpty.java:122)
                at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:129)
                at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:158)
                at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1839)
                at reactor.core.publisher.MonoCacheTime.subscribeOrReturn(MonoCacheTime.java:151)
                at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:57)
                at reactor.core.publisher.MonoZip$ZipCoordinator.request(MonoZip.java:216)
                at reactor.core.publisher.MonoFlatMap$FlatMapMain.request(MonoFlatMap.java:194)
                at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onSubscribe(MonoIgnoreThen.java:134)
                at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:117)
                at reactor.core.publisher.MonoZip.subscribe(MonoZip.java:125)
                at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
                at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
                at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:240)
                at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:203)
                at reactor.core.publisher.MonoFlatMap$FlatMapMain.onComplete(MonoFlatMap.java:189)
                at reactor.core.publisher.Operators.complete(Operators.java:137)
                at reactor.core.publisher.MonoZip.subscribe(MonoZip.java:121)
                at reactor.core.publisher.Mono.subscribe(Mono.java:4444)
                at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:263)
                at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51)
                at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
                at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165)
                at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79)
                at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74)
                at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:82)
                at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.innerNext(FluxConcatMapNoPrefetch.java:258)
                at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:863)
                at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:129)
                at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:180)
                at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2545)
                at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.request(MonoPeekTerminal.java:139)
                at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:171)
                at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.request(Operators.java:2305)
                at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.request(FluxConcatMapNoPrefetch.java:338)
                at reactor.core.publisher.MonoNext$NextSubscriber.request(MonoNext.java:108)
                at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2341)
                at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2215)
                at reactor.core.publisher.MonoNext$NextSubscriber.onSubscribe(MonoNext.java:70)
                at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.onSubscribe(FluxConcatMapNoPrefetch.java:164)
                at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:201)
                at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:83)
                at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
                at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
                at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165)
                at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1839)
                at reactor.core.publisher.MonoCacheTime.subscribeOrReturn(MonoCacheTime.java:151)
                at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:57)
                at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
                at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
                at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
                at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:240)
                at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:203)
                at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:155)
                at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1839)
                at reactor.core.publisher.MonoCacheTime.subscribeOrReturn(MonoCacheTime.java:151)
                at reactor.core.publisher.Mono.subscribe(Mono.java:4429)
                at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:263)
                at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51)
                at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64)
                at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)
                at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165)
                at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79)
                at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:129)
                at reactor.core.publisher.FluxHandleFuseable$HandleFuseableSubscriber.onNext(FluxHandleFuseable.java:193)
                at reactor.core.publisher.MonoFlatMap$FlatMapMain.secondComplete(MonoFlatMap.java:245)
                at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:305)
                at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79)
                at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:129)
                at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onNext(FluxContextWrite.java:107)
                at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onNext(FluxMapFuseable.java:299)
                at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onNext(FluxFilterFuseable.java:337)
                at reactor.core.publisher.Operators$BaseFluxToMonoOperator.completePossiblyEmpty(Operators.java:2071)
                at reactor.core.publisher.MonoCollect$CollectSubscriber.onComplete(MonoCollect.java:145)
                at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144)
                at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260)
                at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144)
                at reactor.netty.channel.FluxReceive.onInboundComplete(FluxReceive.java:413)
                at reactor.netty.channel.ChannelOperations.onInboundComplete(ChannelOperations.java:426)
                at reactor.netty.channel.ChannelOperations.terminate(ChannelOperations.java:480)
                at reactor.netty.http.client.HttpClientOperations.onInboundNext(HttpClientOperations.java:702)
                at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:113)
                at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
                at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
                at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
                at io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:286)
                at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
                at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
                at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
                at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436)
                at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346)
                at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:318)
                at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251)
                at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442)
                at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
                at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
                at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1373)
                at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1236)
                at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1285)
                at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:529)
                at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:468)
                at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:290)
                at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444)
                at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
                at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412)
                at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
                at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440)
                at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420)
                at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
                at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:800)
                at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:499)
                at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:397)
                at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
                at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
                at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
                at java.base/java.lang.Thread.run(Thread.java:833)

Comment From: cjdxhjj

i have dig into the exception, and found out the problem Spring Bean validation fails with ArrayIndexOutOfBoundsException for Kotlin suspending function hibenate replay1 hibenate replay2 the hibenate dosn't adapt kotlin, and consider it was the kotlin term problem, hibernate get the method parameter from the ExecutableParameterNameProvider, which can be configurable by spring. the fact spring special the KotlinReflectionParameterNameDiscoverer to resolve the paramter name, the hibernate get the paramter count at runtime by java reflect api, the count is source parmater count add one , the spring team ignore the continuation parameter, so the paramter is the less one than hibernate, hibernate get all the parameter by index, that cause the problem. it may solved by add one string to getParameterNames() result when detect the method is suspend function. i have wrote a class here.

class KtReflectionParameterNameDiscoverer: KotlinReflectionParameterNameDiscoverer() {
    override fun getParameterNames(method: Method): Array<String>? {
        return super.getParameterNames(method)?.run {
            if (KotlinDetector.isSuspendingFunction(method)) this.plus("continuation") else this
        }
    }
}

i have found that configuration at ValidationAutoConfiguration

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@ConditionalOnMissingBean(Validator.class)
public static LocalValidatorFactoryBean defaultValidator(ApplicationContext applicationContext,
    ObjectProvider<ValidationConfigurationCustomizer> customizers) {
    LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
    factoryBean.setConfigurationInitializer((configuration) -> customizers.orderedStream()
        .forEach((customizer) -> customizer.customize(configuration)));
    MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory(applicationContext);
    factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
    return factoryBean;
}

i'm try to custom a ValidationConfigurationCustomizer to special the KtReflectionParameterNameDiscoverer, but i found i can't special the parameterNameDiscoverer property of LocalValidatorFactoryBean, i have to configuration the LocalValidatorFactoryBean instance

  @Bean
  @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  fun defaultValidator(applicationContext: ApplicationContext): LocalValidatorFactoryBean {
      val factoryBean = LocalValidatorFactoryBean()
      factoryBean.setParameterNameDiscoverer(KtReflectionParameterNameDiscoverer())
      val interpolatorFactory = MessageInterpolatorFactory(applicationContext)
      factoryBean.messageInterpolator = interpolatorFactory.getObject()
      return factoryBean
  }

when i done that things, it works.

Comment From: cjdxhjj

@sbrannen could you fix that problem?

Comment From: cjdxhjj

the KotlinReflectionParameterNameDiscoverer class may used by other place, you can provide a new one, and override the getParameterNames function and replace it in LocalValidatorFactoryBean

public LocalValidatorFactoryBean() {
    if (KotlinDetector.isKotlinReflectPresent()) {
        this.parameterNameDiscoverer = new KotlinReflectionParameterNameDiscoverer();
    }
}

Comment From: sdeleuze

Duplicate of #23499.

Comment From: cjdxhjj

@sdeleuze the issure exist for many years, and dosn't solved

Comment From: bclozel

@cjdxhjj it's not solved yet. This issue is just a duplicate so it's been closed in favor of the other one.

Comment From: cjdxhjj

@sdeleuze @bclozel i have found my solution, that works.

Comment From: cjdxhjj

you may add one switch at KotlinReflectionParameterNameDiscoverer to perform one result for hibernate validate and one for others

Comment From: hantsy

the KotlinReflectionParameterNameDiscoverer class may used by other place, you can provide a new one, and override the getParameterNames function and replace it in LocalValidatorFactoryBean

java public LocalValidatorFactoryBean() { if (KotlinDetector.isKotlinReflectPresent()) { this.parameterNameDiscoverer = new KotlinReflectionParameterNameDiscoverer(); } }

The exception only occurs in KotlinCoroutines which will append an extra Continutation to the fun at runtime.

If you are using pure Kotlin, I do not think there is a problem.

So here, you have to make sure it is a Kotlin coroutine case.

Comment From: cjdxhjj

@hantsy yes, the simple way is to extend that class or add one switch for other case

Comment From: sdeleuze

Let's continue the discussion on #23499 (issue) and #29840 (PR).