Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StackOverflowError with Kotlin Sealed Class Hierarchy and @JsonSubTypes after upgrading to 2.8.5 #2918

Open
machinecode opened this issue Feb 24, 2025 · 0 comments

Comments

@machinecode
Copy link

We are experiencing a StackOverflowError during OpenAPI schema generation after upgrading springdoc-openapi-starter-common and springdoc-openapi-starter-webmvc-ui from version 2.3.0 to 2.8.5. This upgrade was part of a larger update, including Spring Boot from 3.3.7 to 3.4.1. We are using Kotlin and Gradle.

The issue appears to be related to the processing of a sealed class hierarchy with @JsonSubTypes annotation. The structure is similar to the following:

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "@type")
@JsonSubTypes(
    JsonSubTypes.Type(value = ConcreteClassA::class, nams = "TYPE_A"),
    JsonSubTypes.Type(value = ConcreteClassB::class, name = "TYPE_B"),
    // ... more subtypes ...
    JsonSubTypes.Type(value = ConcreteClassWithGrandchild::class, name = "TYPE_WITH_GRANDCHILD"),
    // ... more subtypes ...

)
sealed class AbstractBaseClass {
    abstract fun someAbstractFunction(): String?

    open fun anotherFunction() = someAbstractFunction() ?: ""
}

// Example of a simple subclass
data class ConcreteClassA(
    val someProperty: String?
) : AbstractBaseClass() {
    override fun someAbstractFunction(): String? { /* implementation */ }
}

// Intermediate abstract class
sealed class IntermediateAbstractClass : AbstractBaseClass() {
    abstract val commonProperty1: String?
    abstract val commonProperty2: String?
}
// Example of class extends to another sealed class
data class ConcreteClassWithGrandchild(
    @JsonAlias("alias_prop1")
    @JsonInclude(JsonInclude.Include.NON_NULL)
    override val commonProperty1: String? = null,

    @JsonAlias("alias_prop2")
    @JsonInclude(JsonInclude.Include.NON_NULL)
    val specificProperty: String?,

     override val commonProperty2: String?,

) : IntermediateAbstractClass() {
    override fun someAbstractFunction() = "someValue"
}

The key points are:

  • A sealed class (AbstractBaseClass) is used as the base.
  • @JsonTypeInfo and @JsonSubTypes are used for polymorphic deserialization.
  • There are multiple concrete subclasses, and, importantly, at least one subclass (ConcreteClassWithGrandchild) extends to another sealed class (IntermediateAbstractClass).
  • Some properties use @JsonAlias and @JsonInclude.

The exception occurs during the openApiGenerate Gradle task. The stack trace indicates a stack overflow within org.openapitools.codegen.DefaultCodegen.getAllOfDescendants, suggesting a problem with recursive processing of the class hierarchy.

org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':openApiGenerate'.
  ...
Caused by: org.gradle.api.GradleException: Code generation failed.
  ...
Caused by: java.lang.RuntimeException: Could not process model 'AbstractBaseClass'.Please make sure that your schema is correct!
  ...
Caused by: java.lang.RuntimeException: Stack overflow hit when looking for AbstractBaseClass an infinite loop starting and ending at ConcreteClassWithGrandchild was seen
    at org.openapitools.codegen.DefaultCodegen.getAllOfDescendants(DefaultCodegen.java:3491)
    at org.openapitools.codegen.DefaultCodegen.createDiscriminator(DefaultCodegen.java:3573)
    at org.openapitools.codegen.DefaultCodegen.fromModel(DefaultCodegen.java:3035)
    at org.openapitools.codegen.DefaultGenerator.processModels(DefaultGenerator.java:1765)
    at org.openapitools.codegen.DefaultGenerator.generateModels(DefaultGenerator.java:523)
  ...

This issue seems related to the previously reported and fixed issue: #2603. However, that fix doesn't seem to resolve the problem in our case, potentially because of the grandchild sealed class relationship, or some interaction with the other annotations.

Expected Behavior:
The OpenAPI schema should be generated successfully without a StackOverflowError.

Actual Behavior:
A StackOverflowError occurs during schema generation, preventing the build from completing.

Steps to Reproduce:
While I can't share the exact code, the provided class structure above outlines a simplified version. Creating a similar setup with:

  • Kotlin sealed class.
  • Multiple subtypes.
  • Grandchild in sealed class hierarchy.
  • @JsonTypeInfo, @JsonSubTypes, @JsonAlias, and @JsonInclude annotations.
  • Spring Boot 3.4.1 and springdoc-openapi 2.8.5

should reproduce it.

Environment:

  • Spring Boot: 3.4.1
  • springdoc-openapi-starter-common: 2.8.5
  • springdoc-openapi-starter-webmvc-ui: 2.8.5
  • Kotlin
  • Gradle

Could you please investigate this issue? It's breaking our doc generation completely. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant