ChilliCream / ChilliCream/graphql-platform
OpenAPI adapter: a definition file with several fragments silently loses all but the first
@glen-84 is already working on this.
Since Aug 10, 2026.
- Dominant language
- C#
- Stars
- 5.8k
- Forks
- 810
- Avg merge
- 15h 39m
- Merged PRs (30d)
- 98
Description
Product
Hot Chocolate — HotChocolate.Adapters.OpenApi.Core
Version
16.6.0-p.5
Link to minimal reproduction
https://github.com/alisan3/hc-openapi-adapter-repro
Steps to reproduce
git clone https://github.com/alisan3/hc-openapi-adapter-repro
cd hc-openapi-adapter-repro
dotnet run
No server, gateway or composition involved — the reproduction is one call to the public
OpenApiDefinitionParser.Parse with a definition document holding three error fragments, where the
later two spread the first to share a common selection:
fragment ApiError on Error { message }
fragment NameTooLongError on NameTooLongError { ...ApiError attemptedName }
fragment ThirdError on ThirdError { ...ApiError code }
What is expected?
Either of these would be fine; the current behaviour is neither.
- If a model definition document may hold several fragments, each should end up individually
addressable, so another definition can spread...NameTooLongError. - If exactly one fragment per model definition is the intended contract — which
OpenApiCollectionArchive.AddOpenApiModelAsync(name, document)keying models by name suggests —
then this document is invalid input andParseshould throwOpenApiDefinitionParsingException
naming the offending file.
Separately, ExternalFragmentReferences should not contain a fragment that the definition's own
document defines.
What is actually happening?
Parse returns a single OpenApiModelDefinition named after the first fragment. The remaining
two are demoted into LocalFragmentsByName, i.e. private helpers of ApiError, so no other
definition can reference them by name. An endpoint definition selecting
errors { ...NameTooLongError } gets an external reference that can never resolve — and no error is
raised at parse time.
The returned definition also lists ApiError — its own name, defined in its own document — as an
external dependency. FragmentReferenceFinder.Find(document) reports External = [] for the
same document, but the anchored Find(document, fragment) overload that the parser uses excludes
the anchor from Local and reports it as External instead. This second point looks like a
straightforward bug regardless of how the first is decided.
Relevant log output
== OpenApiDefinitionParser.Parse ==
Name : ApiError
FragmentDefinition : ApiError
LocalFragmentsByName : [NameTooLongError, ThirdError]
ExternalFragmentReferences : [ApiError]
== FragmentReferenceFinder ==
Find(document) : Local=[ApiError, NameTooLongError, ThirdError] External=[]
Find(document, ApiError) : Local=[NameTooLongError, ThirdError] External=[ApiError]
Additional context
We found this building a REST facade over a Fusion gateway with the OpenAPI adapter, where it
surfaces very differently: every write endpoint returned HTTP 500 with an empty body, with nothing
logged. The endpoints were routed, but the definitions referencing the swallowed fragments had
been rejected during registration. Splitting each fragment into its own file fixed all of them at
once.
The cost here was almost entirely diagnostic. Grouping a handful of small error fragments in one
file is a natural thing to do, nothing rejects it, and the failure then shows up far away as an
empty 500. Even just throwing on a multi-fragment document would have made this a one-minute fix.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.