Repository navigation
Collecting recursive schemas fails with a stack overflow in 5.x #1134
Description
Activity
Cargo.tomlfor reference:[package] name = "utoipa-recursion" version = "0.1.0" edition = "2021" [dependencies] utoipa = "5.0.0"Looking at the
cargo expandoutput, this is probably a direct consequence of now recursively collecting subschemas inToSchema::schemas? It could probably be fixed by keeping track of which schemas are in the process of being defined and pass them around while collecting subschemas to break the recursion. But that sounds like a breaking change again.Oh no, this needs some further experimenting to see what can be done with the recursion issue.
#[derive(ToSchema)] pub struct Instance { kind: Kind, } #[derive(ToSchema)] pub enum Kind { MultipleNested(Vec<Instance>), }
Like to code above, the issue is that there is a loop.
Kind->Instance->Kind. And since utoipa 5.0.0 the schemas will be collected recursively, which causes forever loop. This worked in 4 because there was no automatic schema collection.I need to see is there way to recognize compile time whether schema references is already collected thus not trying to collect it again.
It could probably be fixed by keeping track of which schemas are in the process of being defined and pass them around while collecting subschemas to break the recursion. But that sounds like a breaking change again.
Yeah fingers crossed, not necessarily, I have a hunch that I can check before calling
T::schemas(....)thatschemas.contains(T::name())if it does not, then callingT::schemas(...). But this still needs some testing and experimenting with also generic types as well. Namely whether theT::name()will give correct result for looking for schemas.- addedinvestigateFuther investigation needed before other actionFuther investigation needed before other action
on Oct 15, 2024 Yeah fingers crossed, not necessarily, I have a hunch that I can check before calling
T::schemas(....)thatschemas.contains(T::name())if it does not, then callingT::schemas(...).Interesting idea, I think right now, the Vec is empty during the recursing call, because the calling schema is added only after all the dependencies.(scrap that, it wasn't correct)
There's also some concern about code size blow-up because the calling schema has to do these kinds of recursion checks for all of its fields. It would probably be better if the
schemascall would only add itself to the list of collected schemas and then recurse to its children. That way only a single check (for itself) would have to be added to the list. (That change could keep the interface ofschemasstrictly compatible but the behavior would still be incompatible).As long as the user facing behavior / API does not change it's all good. But hard to say as of yet, this still needs some investigation. But quick "fix" for now is to avoid looped references.
Maybe one alternative would be to provide some sort of attribute e.g.
schema(no_recurse)which could be used to break from the recursion.#[derive(ToSchema)] pub struct Instance { #[schema(no_recurse)] kind: Kind, } #[derive(ToSchema)] pub enum Kind { MultipleNested(Vec<Instance>), }
Maybe one alternative would be to provide some sort of attribute e.g.
schema(no_recurse)which could be used to break from the recursion.That would work for us.
- added 3 commits that reference this issue
on Oct 15, 2024 @jrudolph Here is a PR for this feature #1137 Perhaps automated solution can be experimented in future, and optionally enabled via config flag eventually (so people get to choose whether they want to pay the performance cost of checking schema existence). But for now it seems to do the job very well.
- added a commit that references this issue
on Oct 22, 2024 - added a commit that references this issue
on Apr 5, 2026 - added a commit that references this issue
on Jul 18, 2026
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsReleased
We have an api that uses a setup that boils to this:
This fails like that:
in 5.x while it worked in 4.x.
Here's a backtrace from
gdb:Was there any change in how (bi-)recursive schemas are handled?