Skip to content

Stack Overflow / infinite recursion in PolymorphicModelConverter.findComposedSchemas #2801

Closed
@martinitus

Description

@martinitus
Contributor

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)

Activity

bnasslahsen

bnasslahsen commented on Dec 7, 2024

@bnasslahsen
Collaborator

@martinitus,

Not reproducible.
Feel free to provide a Minimal, Reproducible Example - with HelloController that reproduces the problem.

This ticket will be closed, but can be reopened if your provide the reproducible sample.

added
incompleteincomplete description: Make sure you Provide a Minimal, Reproducible Example - with HelloController
on Dec 7, 2024
martinitus

martinitus commented on Dec 7, 2024

@martinitus
ContributorAuthor

@bnasslahsen

Here you go: https://github.com/martinitus/springdoc-openapi-sample

Run app and open localhost:8080/swagger-ui.html. 💥

I've seen you have kotlin tests in here, and I tried to get them going on my non-work windows pc for like 15minutes, but then gave up. Should be easy to add one test scenario that includes this :-)

martinitus

martinitus commented on Jan 2, 2025

@martinitus
ContributorAuthor

@bnasslahsen I think when migrating to OpenAPI v3.1 the workaround I am trying to achieve would no longer be necessary: I was able to get the desired behaviour out of the swagger-core library.

I'll keep experimenting and see if I can get this working with the current springdoc-openapi version.

I also have a small branch that would resolve the infinite recursion, but I'm not sure whether my changes make sense.. I'll add a link to the fork later.

omarfi

omarfi commented on Jan 2, 2025

@omarfi

I am experiencing the same problem after upgrading spring boot to 3.4.1 and springdoc to 2.7.0. I have two self-referential models (ModelA contains a field of type ModelA), and this type of modelling is no longer supported in 2.7.0 it seems. It keeps on recursing in org.springdoc.core.converters.PolymorphicModelConverter#findComposedSchemas.

If I remove these models from my code the error is gone. Also, downgrading to v2.5.0 removes the error.

I have a wild hunch that this commit is the culprit: 497bfae

martinitus

martinitus commented on Jan 2, 2025

@martinitus
ContributorAuthor

It might make sense to add the self referential model scenario as a unit test, as it is unrelated to the solution i have in #2833.

martinitus

martinitus commented on Jan 3, 2025

@martinitus
ContributorAuthor

@omarfi I could not reproduce your issue with a simple self-referential model in the latest main commit after #2833 was merged:

@Schema
data class TreeNode(
	@Schema(description = "The children") val children: List<TreeNode>,
	@Schema(description = "Root nodes don't have a parent") val parent: TreeNode?,
)

@RestController
@RequestMapping("/test")
class TestController {

	@PostMapping("/test")
	fun create(@RequestBody root: TreeNode) {

	}
}

So I guess its resolved :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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

      Participants

      @omarfi@martinitus@bnasslahsen

      Issue actions

        Stack Overflow / infinite recursion in PolymorphicModelConverter.findComposedSchemas · Issue #2801 · springdoc/springdoc-openapi