Skip to content

Stack Overflow / infinite recursion in PolymorphicModelConverter.findComposedSchemas #2801

Closed
@martinitus

Description

@martinitus

Describe the bug
I am trying to work around the problem described here with the workaround described here. TLDR: I want to have a description on a type that is not defined by the type, but by the context where the type is used.

To Reproduce

  • spring-boot 3.3
  • springdoc-openapi 2.70
// sorry for the Kotlin code, my Java is a little outdated...
import io.swagger.v3.oas.annotations.media.Schema
import kotlinx.serialization.Serializable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController


@Serializable
@Schema(description = "Generic description")
data class KeyValue(
    val key: String,
    val value: String,
)

@Serializable
@Schema
data class SomeDTO(
    @Schema(description = "Description A", allOf = [KeyValue::class]) val field_a: KeyValue,
    @Schema(description = "Description B", allOf = [KeyValue::class]) val field_b: KeyValue,
)

@RestController
@RequestMapping("/test")
class TestAdapter() {
    
    @PostMapping("/test")
    fun create(@RequestBody some: SomeDTO) {
        TODO()
    }
}

Expected behavior
Expected Open API Json

... component/schemas/
"SomeDTO": {
        "required": [
          "field_a",
          "field_b"
        ],
        "type": "object",
        "properties": {
          "field_a": {
            "type": "object",
            "description": "Description A",
            "allOf": [
              {
                "$ref": "#/components/schemas/KeyValue"
              }
            ]
          },
          "field_b": {
            "type": "object",
            "description": "Description B",
            "allOf": [
              {
                "$ref": "#/components/schemas/KeyValue"
              }
            ]
          }
        }
      },

Screenshots

It loads (no StackOverflow) and renders correctly, when I change type of field_a and field_b to String. But that's obviously not what I want.

@Schema(description = "Description A", allOf = [KeyValue::class]) val field_a: String,
@Schema(description = "Description B", allOf = [KeyValue::class]) val field_b: String,

image

** Stack Trace**

11:10:45.221 [http-nio-8080-exec-1] /v3/api-docs INFO  d.b...RequestLoggingFilter - method=GET
11:10:45.806 [http-nio-8080-exec-1]  ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed: java.lang.StackOverflowError] with root cause
java.lang.StackOverflowError: null
	at org.springdoc.core.converters.PolymorphicModelConverter.lambda$findComposedSchemas$2(PolymorphicModelConverter.java:147)
	at java.base/java.util.stream.MatchOps$1MatchSink.accept(MatchOps.java:90)
	at java.base/java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1602)
	at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:129)
	at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:527)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:513)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:230)
	at java.base/java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:196)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.anyMatch(ReferencePipeline.java:632)
	at org.springdoc.core.converters.PolymorphicModelConverter.lambda$findComposedSchemas$3(PolymorphicModelConverter.java:147)
	at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:178)
	at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
	at java.base/java.util.TreeMap$ValueSpliterator.forEachRemaining(TreeMap.java:3215)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:575)
	at java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260)
	at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:616)
	at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:622)
	at java.base/java.util.stream.ReferencePipeline.toList(ReferencePipeline.java:627)
	at org.springdoc.core.converters.PolymorphicModelConverter.findComposedSchemas(PolymorphicModelConverter.java:149)

Metadata

Metadata

Assignees

No one assigned

    Labels

    incompleteincomplete description: Make sure you Provide a Minimal, Reproducible Example - with HelloController

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions