Skip to content

[http-client-csharp] ApiCompat can add null guards to nullable reference properties #11814

Description

@JoshLove-msft

Describe the bug

When C# generation uses a previous package through ApiCompatVersion, a required nullable reference-type model property can become nonnullable inside the generator and gain an Argument.AssertNotNull guard.

This was reproduced in openai/openai-dotnet#1341. The TypeSpec declaration remains:

parameters: unknown | null;

Before enabling ApiCompatVersion, the generated FunctionTool constructor accepts null. With the same TypeSpec blob and the same Microsoft.TypeSpec.Generator.ClientModel build, enabling ApiCompatVersion adds:

Argument.AssertNotNull(functionParameters, nameof(functionParameters));

The custom factory and serialization paths still support null, so the generated public constructor becomes inconsistent with both the wire contract and the other creation path.

Root cause

LastContractView obtains reference types from Roslyn through TypeSymbolExtensions.GetCSharpType, which does not retain nullable-reference annotations. A nullable-oblivious last-contract BinaryData is therefore represented with IsNullable == false.

ModelProvider.BuildProperties treats that as a meaningful nullability difference and replaces the current nullable TypeSpec-derived property type with the last-contract type. When the lazy constructor parameter is later created, ParameterProvider.GetParameterValidation sees a nonnullable reference type and emits AssertNotNull.

Scope

This can affect any required nullable reference-type model property when last-contract compatibility is enabled. The same OpenAI regeneration also added a guard for a bytes | null property. Nullable collection properties can additionally lose null-safe constructor conversion.

Expected behavior

Nullable-oblivious last-contract reference types should not override current TypeSpec reference nullability. Value-type nullability compatibility should remain unchanged.

Suggested coverage

  • required unknown | null against an oblivious BinaryData last-contract property
  • required bytes | null
  • required nullable collection conversion
  • required nonnullable reference property as a negative case

Related review: openai/openai-dotnet#1341 (comment)

Activity

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

Metadata

Metadata

Assignees

Labels

emitter:client:csharpIssue for the C# client emitter: @typespec/http-client-csharp

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions