Skip to content

[Bug]: [http-server-csharp] @error model with a numeric-literal property emits a CS1763-invalid constructor (object x = N default) #10713

@mkaring

Description

@mkaring

Describe the bug

Summary

When an @error model has a property whose type is a non-string literal (e.g. status: 404), @typespec/http-server-csharp emits a constructor whose default parameter is typed object with the literal as its default value:

public NotFoundProblem(string title, object status = 404) : base(400, ...)

This violates C# language rule CS1763: a default parameter of a reference type other than string may only be initialised with null. The generated file does not compile.

Within the same generated class the property itself is correctly typed int:

public int Status { get; } = 404;

— so the in-class typing is inconsistent even before CS1763 fires.

Versions

Package Version
@typespec/compiler 1.12.0
@typespec/http 1.12.0
@typespec/rest 0.82.0
@typespec/http-server-csharp 0.58.0-alpha.28
Node 22.x / 24.x (both confirmed)
.NET SDK 10.0.203 (any LTS)

Trigger

The bug requires all three:

  1. A model decorated with @error.
  2. A property on that model whose type is a non-string literal (numeric or boolean).
  3. An op that references the @error model in its return type so the emitter actually emits it.

Plain models (without @error) emit literal-typed properties cleanly as read-only properties — only @error triggers the all-fields-constructor code path that produces the broken signature.

Minimal repro source

import "@typespec/http";
import "@typespec/rest";

using Http;
using Rest;

@service(#{ title: "Repro" })
namespace Repro;

// Control: plain model with the same literal pattern — emits cleanly.
model Config {
    name: string;
    version: 1;
}

// Trigger: @error model with a numeric-literal-typed property.
@error
model NotFoundProblem {
    title: string;
    status: 404;
}

@route("/config")
interface ConfigApi {
    @get
    @route("{id}")
    get(@path id: string): Config | NotFoundProblem;
}

What gets emitted

Config.csthe control, clean:

public partial class Config
{
    public string Name { get; set; }
    public int Version { get; } = 1;     // ✅ legal
}

NotFoundProblem.csthe bug:

public partial class NotFoundProblem : HttpServiceException
{
    public NotFoundProblem(string title, object status = 404) : base(400,   // ← CS1763
       value: new { title = title, status = status })
    {
        Title = title;
        Status = status;                  // ← would also fail (CS0029)
                                          //   if CS1763 weren't first
    }
    public string Title { get; set; }

    public int Status { get; } = 404;     // property is `int`, not `object` —
                                          // inconsistent intra-class typing
}

Build error

NotFoundProblem.cs(15,53): error CS1763:
  "status" is of type "object". A default parameter value of a reference type
  other than string can only be initialized with null.

(Column 53 is the = 404 token.)

Expected behaviour

One of:

  • Drop the literal-ness from the constructor signature and use the base scalar:
    public NotFoundProblem(string title, int status = 404) : base(404, ...)
  • Or fold the literal-typed property out of the constructor signature entirely (it is already pinned to 404 via the property initialiser):
    public NotFoundProblem(string title) : base(404, ...) { ... }

Either is valid C# and preserves the TypeSpec contract.

Workaround

Replace the literal with the base scalar type:

 @error
 model NotFoundProblem {
     title: string;
-    status: 404;
+    status: int32;
 }

The trade-off: the TypeSpec source loses the per-subtype status discriminator the API contract intended. RFC-9457-style problem-detail subtypes ("this variant always means HTTP 404") cannot be expressed.

Reproduction

The Playground does not run @typespec/http-server-csharp, so a standalone repo is needed: https://github.com/TKI-Chemnitz/typespec-http-server-csharp-cs1763-repro.

The repo contains exactly the minimal pieces:

  • main.tsp — ~20 lines (control model + @error trigger model + one op).
  • package.json — exact version pins (no caret/tilde).
  • pnpm-lock.yaml — lockfile committed.
  • tspconfig.yaml — minimal, only the C# server emitter.
  • Repro.csproj — net10.0 library, ImportDirectoryBuildProps=false, FrameworkReference Microsoft.AspNetCore.App.

To reproduce:

corepack pnpm install
corepack pnpm exec tsp compile main.tsp
dotnet build Repro.csproj

Or in one go:

corepack pnpm run repro

Expected output: dotnet build exits with one CS1763 error pointing at tsp-output/@typespec/http-server-csharp/generated/models/NotFoundProblem.cs(15,53).

Checklist

Metadata

Metadata

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions