When upgrading to Spring Boot 3.2.1 (from 3.1.7) we encountered a bug that throws an exception saying "object is not an instance of declaring class".

After a lot of dogging we've found out that the excpeption only occured when we had org.springframework:spring-tx on the classpath. Acutually, org.springframework:spring-tx is a transitive dependency from org.springframework:spring-jms that we are using.

Environment

Spring Framework Version: 6.1.2 (with spring-boot 3.2.1) Kotlin Version: 1.9.22 JVM Version: 17

Code

build.gradle.kts

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    id("org.springframework.boot") version "3.2.1"
    id("io.spring.dependency-management") version "1.1.4"
    kotlin("jvm") version "1.9.22"
    kotlin("plugin.spring") version "1.9.22"
}

group = "se.lantmateriet.tacodus.orderhanteraremock"
version = "0.0.1-SNAPSHOT"

java {
    sourceCompatibility = JavaVersion.VERSION_17
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-webflux")
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
    implementation("io.projectreactor.kotlin:reactor-kotlin-extensions")
    implementation("org.jetbrains.kotlin:kotlin-reflect")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactor")
    implementation("org.apache.httpcomponents:httpclient:4.5.14")

    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("io.projectreactor:reactor-test")

    implementation("org.springframework:spring-tx")
}

tasks.withType<KotlinCompile> {
    kotlinOptions {
        freeCompilerArgs += "-Xjsr305=strict"
        jvmTarget = "17"
    }
}

tasks.withType<Test> {
    useJUnitPlatform()
}

Application

@SpringBootApplication
class ValueClassBugApplication

fun main(args: Array<String>) {
    runApplication<ValueClassBugApplication>(*args)
}

@RestController
@RequestMapping(("/foo"))
class FooController(
    private val fooRepository: FooRepository
) {

    @GetMapping("")
    suspend fun foo() {
        fooRepository.doSomething(AValueClass("foo"))
    }
}

//@Service
@Repository
class FooRepository {
    suspend fun doSomething(aValueClass: AValueClass) {
        //
    }
}

@JvmInline
value class AValueClass(val value: String)

Start the application and make a GET request;

curl -i localhost:8080/foo

Expected behavior

Server returns 200

Actual Behavior

The server returns 500 and gets an internal server error with an stacktrace:

2023-12-22T14:52:20.865+01:00 ERROR 1626972 --- [         task-1] a.w.r.e.AbstractErrorWebExceptionHandler : [71ea1fd0-1]  500 Server Error for HTTP GET "/foo"

java.lang.IllegalArgumentException: object is not an instance of declaring class
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
    *__checkpoint ⇢ Handler se.lantmateriet.tacodus.orderhanteraremock.valueclassbugg.FooController#foo(Continuation) [DispatcherHandler]
    *__checkpoint ⇢ HTTP GET "/foo" [ExceptionHandlingWebHandler]
Original Stack Trace:
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
        at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
        at kotlin.reflect.jvm.internal.calls.ValueClassAwareCaller.call(ValueClassAwareCaller.kt:190) ~[kotlin-reflect-1.9.22.jar:1.9.22-release-704]
        at kotlin.reflect.jvm.internal.KCallableImpl.callDefaultMethod$kotlin_reflection(KCallableImpl.kt:207) ~[kotlin-reflect-1.9.22.jar:1.9.22-release-704]
        at kotlin.reflect.full.KCallables.callSuspendBy(KCallables.kt:74) ~[kotlin-reflect-1.9.22.jar:1.9.22-release-704]
        at org.springframework.core.CoroutinesUtils.lambda$invokeSuspendingFunction$2(CoroutinesUtils.java:124) ~[spring-core-6.1.2.jar:6.1.2]
        at kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$4.invokeSuspend(IntrinsicsJvm.kt:270) ~[kotlin-stdlib-1.9.22.jar:1.9.22-release-704]
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) ~[kotlin-stdlib-1.9.22.jar:1.9.22-release-704]
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
        at kotlinx.coroutines.EventLoop.processUnconfinedEvent(EventLoop.common.kt:68) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
        at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:375) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
        at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
        at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:25) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
        at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:110) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
        at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126) ~[kotlinx-coroutines-core-jvm-1.7.3.jar:na]
        at kotlinx.coroutines.reactor.MonoKt.monoInternal$lambda$2(Mono.kt:92) ~[kotlinx-coroutines-reactor-1.7.3.jar:na]
        at reactor.core.publisher.MonoCreate.subscribe(MonoCreate.java:61) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.FluxFromMonoOperator.subscribe(FluxFromMonoOperator.java:85) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.FluxDeferContextual.subscribe(FluxDeferContextual.java:57) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoSubscribeOn$SubscribeOnSubscriber.onNext(MonoSubscribeOn.java:146) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:200) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.complete(MonoIgnoreThen.java:293) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onNext(MonoIgnoreThen.java:188) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:158) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoZip$ZipCoordinator.signal(MonoZip.java:297) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoZip$ZipInner.onNext(MonoZip.java:478) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:180) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2571) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.request(MonoPeekTerminal.java:139) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoZip$ZipInner.onSubscribe(MonoZip.java:470) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onSubscribe(MonoPeekTerminal.java:152) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoZip$ZipCoordinator.request(MonoZip.java:220) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.request(MonoFlatMap.java:194) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onSubscribe(MonoIgnoreThen.java:135) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:117) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoZip.subscribe(MonoZip.java:129) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:53) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:241) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:204) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoFlatMap$FlatMapMain.onComplete(MonoFlatMap.java:189) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.Operators.complete(Operators.java:137) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoZip.subscribe(MonoZip.java:121) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:76) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:264) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.Mono.subscribe(Mono.java:4512) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.publisher.MonoSubscribeOn$SubscribeOnSubscriber.run(MonoSubscribeOn.java:126) ~[reactor-core-3.6.1.jar:3.6.1]
        at reactor.core.scheduler.ExecutorScheduler$ExecutorTrackedRunnable.run(ExecutorScheduler.java:192) ~[reactor-core-3.6.1.jar:3.6.1]
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[na:na]
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[na:na]
        at java.base/java.lang.Thread.run(Thread.java:840) ~[na:na]

If I change @Repository to @Service it works as expected.

Regards, Peter

Comment From: sdeleuze

Can you please test with Spring Framework 6.1.3-SNAPSHOT?

Comment From: pwestlin

Can you please test with Spring Framework 6.1.3-SNAPSHOT?

How do I accomplish that in a manageable way? :) I've tried with Spring Boot 3.2.2-SNAPSHOT but that includes Spring 6.1.2, just like Spring Boot 3.2.1.

Comment From: quaff

Can you please test with Spring Framework 6.1.3-SNAPSHOT?

How do I accomplish that in a manageable way? :) I've tried with Spring Boot 3.2.2-SNAPSHOT but that includes Spring 6.1.2, just like Spring Boot 3.2.1.

You can set spring-framework.version=6.1.3-SNAPSHOT in gradle.properties, and make sure snapshot maven repository is included.

    maven {
        url "https://repo.spring.io/snapshot"
    }

Comment From: pwestlin

Can you please test with Spring Framework 6.1.3-SNAPSHOT?

It works with spring-framework.version=6.1.3-SNAPSHOT.

Comment From: bclozel

Thanks for letting us know, this means this has been fixed already in #31846 I'll close this issue as a duplicate, then.