Skip to content

build(deps): bump Microsoft.OpenApi to 3.10.0 - #8050

Merged
Vincent Biret (baywet) merged 4 commits into
microsoft:mainfrom
EelcoLos:openapi-3.10-upgrade
Aug 13, 2026
Merged

build(deps): bump Microsoft.OpenApi to 3.10.0#8050
Vincent Biret (baywet) merged 4 commits into
microsoft:mainfrom
EelcoLos:openapi-3.10-upgrade

Conversation

@EelcoLos

Copy link
Copy Markdown
Contributor

Why

Follow up to the discussion on #8038. Microsoft.OpenApi 3.10.0 has been out for a while and we are still on 3.9.0, and I wanted to establish exactly what the upgrade costs before the 1.29 security branch made any assumptions about it.

What

Two lines.

  1. Microsoft.OpenApi and Microsoft.OpenApi.YamlReader 3.9.0 to 3.10.0.
  2. 3.10.0 obsoletes IOpenApiSchema.Example in favour of Examples, and we treat that warning as an error. The only usage is the schema copy in PluginsGenerationService, so it copies Examples now.

That is the entire upgrade. Full suite is green, dotnet format --verify-no-changes is clean.

On the anyOf concern

The worry raised in #8038 was that the newer readers fold anyOf: [string, integer] into a single flags-enum Type and null out AnyOf, which would stop SelectFirstAnyOneOfVisitor from ever firing. Since JsonSchemaType is a flags enum, the authoring order is not recoverable once that happens.

That does not affect the 3.x line. GeneratesManifestAndCleansUpInputDescriptionAsync asserts AnyOf.Count == 2 on the freshly loaded source document, and it still passes on 3.10.0.

It is real on the 2.x line: pinning the 1.29 branch to 2.12.0 makes that same assertion throw a NullReferenceException because AnyOf is null, against a byte identical fixture. So it is a 2.12.0 specific behaviour, not something inherited by 3.10.0.

For the 1.29 branch that means 2.11.0 is the right target rather than 2.12.0, which is what #8038 now does. No behavioural decision is needed in either place.

3.10.0 obsoletes IOpenApiSchema.Example in favour of Examples, which
is an error under our warnaserror settings. The one usage is the
schema copy in PluginsGenerationService, so it now copies Examples.

Verified the anyOf handling is unaffected: the plugin manifest test
still sees AnyOf populated on the source document, and the whole
suite is green on 3.10.0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUifZksSV3pi2zWxvs29ka

@baywet Vincent Biret (baywet) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution!

@github-actions

Copy link
Copy Markdown
Contributor

Thanks for opening this pull request! Please add a changelog entry under ## [Unreleased] in CHANGELOG.md, before the first released version section, so users can see what changed. Add bug fixes under ### Changed and new features under ### Added.

auto-merge was automatically disabled August 13, 2026 12:41

Head branch was pushed to by a user without write access

3.10.0 folds a union of primitive types, anyOf: [string, integer], into
a single Type carrying both flags and clears AnyOf, so
SelectFirstAnyOneOfVisitor never sees the union and the generated plugin
description kept the multi-valued type. I missed this in the bump commit
because the local test host was crashing partway through the run and
still reporting the completed tests as passing.

NarrowMultipleTypes collapses it back to a single type. A numeric type
paired with a string and a numeric format is what System.Text.Json's
JsonNumberHandling.AllowReadingFromString advertises, so the numeric type
wins there, matching what GetPrimitiveType already does for clients.
Otherwise the string wins.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUifZksSV3pi2zWxvs29ka
auto-merge was automatically disabled August 13, 2026 13:09

Head branch was pushed to by a user without write access

@EelcoLos

Copy link
Copy Markdown
Contributor Author

Correction, and it changes this PR materially, so please re-review rather than relying on the earlier approval.

I was wrong that 3.10.0 does not fold primitive unions. It does. The claim I made on #8038, and repeated here, does not hold.

What went wrong on my side: my local test runs were reporting green while the test host was crashing partway through, exit code -1073741571, and the runner still prints Passed! for whatever completed before the crash. The test counts drifted between runs, 1302 then 818 then 982 against CI's 1409, and I did not chase that discrepancy. CI on this PR caught it at exactly the assertion I had cited as proof, PluginsGenerationServiceTests line 299.

So both readers fold, and the difference I reported between the 2.x and 3.x lines was an artefact of my broken local runs, not real behaviour.

I pushed the fix here rather than just the bump. NarrowMultipleTypes in SelectFirstAnyOneOfVisitor collapses a multi flag Type back to a single type, and the source side assertion now pins the folded shape instead of the pre 3.10 one.

The narrowing rule follows your guidance on #8038: a numeric type paired with a string and a numeric format resolves to the numeric type, correlating the format first, and a string only wins when there is no numeric format to go on. Rather than invent a precedence I reused the rule GetPrimitiveType already applies for clients from #6541, so the plugin path and the client path now agree.

PluginsGenerationServiceTests 106 passed, integration tests 125 passed, dotnet format clean. I am deliberately not claiming a full local suite pass given the crash above, CI is the judge here.

A component schema with no explicit discriminator mapping maps to itself,
so resolving that mapping from CreateComposedModelDeclaration re-entered
the method for the same schema and recursed until the stack overflowed.
AddDiscriminatorMethodIfNeeded already guards the same cycle, the composed
path did not.

Reproduces on 3.10.0 with TypeScriptLanguageRefinerTests.ParsesAndRefines
UnionOfPrimitiveValuesAsync, which crashed the test host rather than
failing, so the runner still reported the tests it had finished as passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VUifZksSV3pi2zWxvs29ka
@EelcoLos

Copy link
Copy Markdown
Contributor Author

CI caught a second problem, and it is not caused by the plugin fix. Pushed c0866d036.

dotnet-build failed with a stack overflow, not an assertion:

KiotaBuilder.CreateComposedModelDeclaration
  -> GetDiscriminatorMappings
  -> GetCodeTypeForMapping
  -> CreateModelDeclarations
  -> CreateComposedModelDeclaration   (repeats until the stack is gone)

Bisected locally on the main worktree:

commit contents result
4bc8675d4 baseline, OpenApi 3.9.0 2237 passed, no crash
5e45ea7c4 the 3.10.0 bump alone, no code change test host crash
c0866d036 with the guard below 2239 run, 0 failed

Reduced to a single deterministic repro: TypeScriptLanguageRefinerTests.ParsesAndRefinesUnionOfPrimitiveValuesAsync, whose fixture is a component schema with oneOf: [string, number]. It crashes in isolation on 3.10.0 and passes on 3.9.0.

Cause: when a composed schema carries no explicit discriminator mapping, OpenApiSchemaExtensions.cs:451 emits a self mapping (refId to refId). CreateComposedModelDeclaration resolves that mapping, which re-enters CreateComposedModelDeclaration for the same schema. AddDiscriminatorMethodIfNeeded already guards this exact cycle with schemasBeingProcessedForDiscriminators, the composed path did not. 3.10.0 changes what that code path sees, so the pre-existing hole started being reached.

Fix: reuse the same visited set guard in the composed path. No new mechanism, and no new test, since the crashing test is already in the suite and is the regression check.

Worth flagging about the failure mode: the runner printed Passed! Failed: 0 for everything that finished and only Test Run Failed at the very bottom. That is the same signature that misled me earlier in this PR, so I now check the tail of the log rather than the summary line.

Local verification on c0866d036: Kiota.Builder.Tests 2230 passed, 0 failed, 2 skipped, no crash, excluding KiotaSearcherTests which calls GitHub live and gets rate limited from here. Kiota.Builder.IntegrationTests 125 passed, Kiota.Tests 18 passed, dotnet format clean.

For the record, #8038 is unaffected. It pins 2.12.0 and its run settled at 289 of 289 green.

@baywet Vincent Biret (baywet) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for making the changes!

@baywet
Vincent Biret (baywet) added this pull request to the merge queue Aug 13, 2026
Merged via the queue into microsoft:main with commit 073504c Aug 13, 2026
311 checks passed
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

Successfully merging this pull request may close these issues.

2 participants