Skip to content

Unable to use kotlin value classes as function arguments to functions mapping web endpoints #31698

Closed
@junkdog

Description

@junkdog

I've encountered a bug in Spring Framework version 6.1.0 related to Kotlin value classes in web endpoints. The issue arises when a Kotlin value class is used as a path variable in a Spring Boot controller. This results in a failure to correctly handle the value type, causing runtime exceptions.

Environment

Spring Framework Version: 6.1.0 (with spring-boot 3.2.0)
Kotlin Version: 1.9.20
JVM Version: 17

Steps to reproduce

  1. Define a Spring Boot controller with two endpoints, one accepting a Kotlin data class as a path variable and another accepting a Kotlin value class.
  2. Create tests for these endpoints using SpringBootTest.
  3. Observe that the endpoint with the Kotlin data class functions correctly, while the one with the kotlin value class fails.

Expected behavior

Both endpoints should accept their respective path variables without any issue

Actual Behavior

The endpoint with the Kotlin value class as a path variable fails at runtime with the error message "object is not an instance of declaring class".

Minimal example

@RestController
@RequestMapping(value = ["/exhibit"])
class KotlinCallByBugController {

    @GetMapping("/working-id/{id}")
    fun works(
        @PathVariable id: WorkingId
    ) = Unit

    @GetMapping("/value-id/{id}")
    fun broken(
        @PathVariable id: SomeId
    ) = Unit
}

@JvmInline // "Value classes without @JvmInline annotation are not supported yet"
value class SomeId(val s: String)

// data classes work just fine
data class WorkingId(val s: String)
@SpringBootTest(
    webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
    classes = [MyApplication::class])
class KotlinCallByBugControllerTest {
    @LocalServerPort
    protected val port = 0

    @Test
    fun `this works - call endpoint with path variable as data class`() {
        val client = HttpClients.createDefault()
        client.execute(HttpGet("http://localhost:$port/exhibit/working-id/123")) { response ->
            assertThat(response.code).isEqualTo(200)
        }
    }

    @Test
    fun `this does not work - call endpoint with path variable as value class`() {
        // breaks: "object is not an instance of declaring class"
        val client = HttpClients.createDefault()
        client.execute(HttpGet("http://localhost:$port/exhibit/value-id/123")) { response ->
            assertThat(response.code).isEqualTo(200)
        }
    }
}

Metadata

Metadata

Assignees

Labels

in: webIssues in web modules (web, webmvc, webflux, websocket)theme: kotlinAn issue related to Kotlin supporttype: bugA general bug

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions