Skip to content

fix(http-server-csharp): make optional error properties nullable - #11895

Open
sophia-ramsey wants to merge 4 commits into
mainfrom
sramsey/csharp-optional-error-properties
Open

sophia-ramsey wants to merge 4 commits into
mainfrom
sramsey/csharp-optional-error-properties

Conversation

@sophia-ramsey

Copy link
Copy Markdown
Member

This pull request fixes the handling of optional properties on C# error models in the @typespec/http-server-csharp package. Now, optional error model properties and their constructor parameters are emitted as nullable types in C#, ensuring better alignment with C# conventions and improved correctness. The changes also ensure that only error models receive this nullable treatment for optional properties, leaving other models unchanged. Comprehensive tests were added to validate this behavior.

C# Error Model Nullability Fixes:

  • Optional properties and constructor parameters in C# error models are now emitted as nullable types (e.g., string?, int?) when marked optional or explicitly nullable in the TypeSpec model. [1] [2] [3]
  • The logic for determining nullability in generated code was updated to ensure correct handling of optional and nullable union types, and to avoid double-nullable suffixes. [1] [2]

Testing Improvements:

  • Added a new test suite in error-models.test.tsx to verify that optional error properties and constructor parameters are correctly emitted as nullable, and that non-error models are unaffected.

Codebase Consistency:

  • Updated documentation and comments to clarify that nullable reference types for properties are limited to error models.
  • Refactored imports to use getNullableUnionInnerType for improved code clarity and maintainability. [1] [2]

@pkg-pr-new

pkg-pr-new Bot commented Sep 9, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@typespec/http-server-csharp@11895

commit: e73eceb

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

All changed packages have been documented.

  • @typespec/http-server-csharp
Show changes

@typespec/http-server-csharp - fix ✏️

Emit nullable C# property and constructor parameter types for optional error model properties.

@typespec/http-server-csharp - fix ✏️

Treat operations returning only error union variants as bodyless responses.

@azure-sdk-automation

azure-sdk-automation Bot commented Sep 9, 2026

Copy link
Copy Markdown

You can try these changes here

🛝 Playground 🌐 Website 🛝 VSCode Extension

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@sophia-ramsey
sophia-ramsey force-pushed the sramsey/csharp-optional-error-properties branch from 8468205 to f21fecd Compare September 14, 2026 23:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

A critical nested-union nullability issue, an inherited-property mismatch, and duplicate fix changesets remain unresolved.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Updates the C# HTTP server emitter to generate nullable optional properties and constructor parameters for error models, with related response handling and tests.

Changes:

  • Adds nullable handling for error-model properties and constructors.
  • Covers nullable models and error-only responses with tests.
  • Adds Chronus changelog entries.
File summaries
File Reviewed changes
packages/http-server-csharp/test/generation.test.ts Updates generated constructor expectations.
packages/http-server-csharp/src/components/models/models.tsx Applies nullable handling to error properties; critical nested-union nullability issue remains.
packages/http-server-csharp/src/components/models/error-models.tsx Updates error constructor parameters; moderate inherited-property mismatch remains.
packages/http-server-csharp/src/components/models/error-models.test.tsx Adds error-model nullability coverage.
packages/http-server-csharp/src/components/controller-action/response-analysis.ts Handles error-only response unions as bodyless responses.
packages/http-server-csharp/src/components/controller-action/controller-action.test.tsx Tests error-only response behavior.
.chronus/changes/sramsey-csharp-optional-error-properties-2026-8-14-17-1-42.md Adds a fix changeset; duplicate changesets should be consolidated.
.chronus/changes/fix-csharp-optional-error-properties-2026-09-08.md Documents the related fix.
Review details

Suppressed comments (2)

.chronus/changes/sramsey-csharp-optional-error-properties-2026-8-14-17-1-42.md:4

  • These two added changesets both declare a fix for @typespec/http-server-csharp. Keep one changeset per package/change type by combining these messages (or move the separate response-analysis fix to another PR); otherwise this release will contain duplicate package fix entries and the PR description will not reflect all included behavior.
changeKind: fix
packages:
  - "@typespec/http-server-csharp"

packages/http-server-csharp/src/components/models/error-models.tsx:68

  • getAllProperties includes properties declared on non-error base models (model-helpers.ts:255-266), but ServerProperty only applies the new nullable property treatment when the declaring/rendered class is an error (models.tsx:154-163,213-245). An @error model extending a regular base therefore gets a nullable constructor parameter here that is assigned to a non-nullable base property; with #nullable enable this creates a nullable-flow warning and a mismatched generated contract. Derive the constructor parameter nullability from the declaring property's emitted type, or update both declaration and constructor together, and cover the inherited case.
      prop.type.kind === "Union" ? getNullableUnionInnerType(prop.type) : undefined;
    const typeExpressionIncludesNullable =
      getNullableValueTypeUnionInnerType($, prop.type) !== undefined;
    const needsNullable =
      !typeExpressionIncludesNullable && (prop.optional || nullableUnionInnerType !== undefined);
  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/http-server-csharp/src/components/models/models.tsx Outdated
Copilot AI review requested due to automatic review settings September 15, 2026 01:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Address the critical header null-handling defect and related scope, inheritance, documentation, and duplicate changeset issues.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (4)

Previously missed (2) — in code that hasn't changed since the last review.

packages/http-server-csharp/src/components/controller-action/response-analysis.ts:77

  • This hunk changes controller generation for a separate case: a union with no value/void/bodyless success now emits a 204/NoContent response. The PR title and description only describe nullable error-model properties, so please split this behavior into a separate change or document and intentionally cover this additional output change in the PR scope.
    .chronus/changes/fix-csharp-optional-error-properties-2026-09-08.md:4
  • Both new changeset files declare the same fix change for @typespec/http-server-csharp. The repository changelog guidance requires one entry per package and change type, so combine these two user-facing messages into a single fix entry to avoid duplicate release entries.

packages/http-server-csharp/src/components/models/error-models.tsx:68

  • This also marks inherited properties nullable: getErrorConstructor walks getAllProperties, but an optional reference property declared on a non-error base is still emitted by ServerProperty without ?. The existing Base/ApiError case therefore assigns a nullable data parameter to a non-nullable base property under #nullable enable, producing a nullability warning; only apply this to properties declared by the error model or emit the base property with matching nullability.
    const needsNullable =
      !typeExpressionIncludesNullable && (prop.optional || nullableUnionInnerType !== undefined);

packages/http-server-csharp/src/components/models/models.tsx:183

  • The class-level documentation above still says there is no nullable ? suffix on reference types, which is now false for error models. Update that comment as well so it states the error-model exception instead of contradicting this new clarification.
 * No `required` or `[JsonPropertyName]`. Nullable reference types are limited to error models.
  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread packages/http-server-csharp/src/components/models/error-models.tsx
Copilot AI review requested due to automatic review settings September 15, 2026 02:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The unresolved nullable dictionary issue can cause warnings-as-errors failures; documentation and scope nits also remain.

Review details

Suppressed comments (3)

packages/http-server-csharp/src/components/controller-action/response-analysis.ts:77

  • This hunk changes existing emitter output for operations whose return union contains only errors: they now generate a 204/no-body action instead of the previous value response. The separate changeset documents that behavior, but the PR title and description only cover error-model nullability; please split this independent fix or document its scope and rationale in the PR.
    if (hasVoidSuccess || !hasValueSuccess) {

packages/http-server-csharp/src/components/models/error-models.tsx:68

  • When this branch handles an optional @header, it emits a string? constructor parameter and then inserts it into headers: new(). HttpServiceException still declares Dictionary<string, string> for both the constructor and Headers property, so the generated file is emitted under #nullable enable with a possible-null-to-nonnullable value warning (and fails projects that treat warnings as errors). Make the exception/header dictionary types accept string? consistently, including the child-error constructor path.
    const needsNullable =
      !typeExpressionIncludesNullable && (prop.optional || nullableUnionInnerType !== undefined);

packages/http-server-csharp/src/components/models/models.tsx:183

  • The class-level documentation above still says this component emits no nullable ? suffixes for reference types, but this new error-model path now does so. Please scope that statement to non-error models (or otherwise update it) so the class and property documentation remain consistent.
 * No `required` or `[JsonPropertyName]`. Nullable reference types are limited to error models.
  • Files reviewed: 10/11 changed files
  • Comments generated: 0 new
  • Review effort level: Lite

@sophia-ramsey
sophia-ramsey added this pull request to the merge queue Sep 15, 2026
IDictionary<string, int> counts = default,
int attempts = default,
string[] tags = default,
string? param = default,

@timotheeguerin Timothee Guerin (timotheeguerin) Sep 15, 2026

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.

actually, is that correct? can optional parameters have default in c#?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think yes; the default will end up being null for optional types

@timotheeguerin
Timothee Guerin (timotheeguerin) removed this pull request from the merge queue due to a manual request Sep 15, 2026
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.

3 participants