spring-projects / spring-projects/spring-framework

IllegalArgumentException: argument type mismatch": Delegate 'by' pattern breaks when property type is a nullable Value Class

Open
#36,020 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

in: core status: waiting-for-triage
Dominant language
Java
Stars
60.2k
Forks
38.8k
Avg merge
5d 2h
Merged PRs (30d)
27

Description

If I have something like this:


@Repository
internal class ContractDomainRepositoryImpl (
    private val contractRepositoryImpl: ContractRepositoryImpl,
    private val contractDomainEntityMapper: ContractDomainEntityMapperImpl,
) : ContractDomainRepository,
    ContractDomainPostRepository by ContractDomainPostRepositoryDelegate(
        contractRepositoryImpl,
        contractDomainEntityMapper
    ),
    ContractDomainPutRepository by ContractDomainPutRepositoryDelegate(
        contractRepositoryImpl,
        contractDomainEntityMapper
    ),
    ContractDomainDeleteRepository by ContractDomainDeleteRepositoryDelegate(
        contractRepositoryImpl
    )

And the interface for ContractDomainPutRepository looks something like:

interface ContractDomainPutRepository {

    suspend fun updateContract(
        contract: Contract,
        operationalData: ContractOperationalData,
        userId: UserId,
        organisationId: OrganisationId?,
        projectId: ProjectId?,
    ): Pair<Contract, ContractOperationalData>

}

And UserId is a value class, like:

@JvmInline
value class UserId(override val value: Long) : Identifier<Long> {
    init {
        // TODO() here it is ok for UserId to be 0, for testing purposes only. Adjust later!
        if (value < 0) {
            throw ValidationError(
                message = "UserId must be a positive value",
            )
        }
    }
},

then trying to call ContractDomainPutRepository.updateContract (from the service layer, that injects ContractDomainRepositoryImpl) will not work

I keep getting Exception caught in ContractDomainServiceImpl: java.lang.IllegalArgumentException: argument type mismatch

Removing the nullable Value Classes, like:

interface ContractDomainPutRepository {

    suspend fun updateContract(
        contract: Contract,
        operationalData: ContractOperationalData,
        userId: UserId,
        organisationId: OrganisationId,
        projectId: ProjectId,
    ): Pair<Contract, ContractOperationalData>

}, 

makes the issue go away

But I need some of value classes to be nullable

The root cause I believe is that Spring's CGLIB proxying doesn't work well with Kotlin value classes since they're implemented as inline classes at compile time and have special JVM representations.

LLM Claude

If you LLM Claude with: "Value classes result it wrong type mismatch error in java class when passed as parameter in spring annotated bean. A plain class works fine. Explain". It says:

When Spring tries to:

Create proxies (CGLIB or JDK proxies)
Inject dependencies
Call methods from Java code

It sees the mangled signatures and underlying primitive types, not the value class wrapper. This causes type mismatches.

Key Takeaway

Value classes are optimized for performance by avoiding object allocation, but this optimization conflicts with Spring's reflection-heavy, proxy-based dependency injection, especially when Java interop is involved. For Spring beans, regular classes are safer unless you carefully manage where value classes are used.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the provided ContractDomainRepository delegation and the updateContract call using nullable and non-nullable Kotlin value classes. Reproduce the IllegalArgumentException and compare the generated or proxied method argument types; done means the nullable value-class parameters invoke successfully without the argument type mismatch.

Written by the indexing model from the issue text.

Assessment

Tech stack
kotlin, spring
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.