Skip to content

[Bug]: C# emitter does not guard against null ToString() when serializing exploded query parameters backed by string structs #11431

Description

@joseharriaga

Describe the bug

When a TypeSpec model property is a string-backed struct (e.g. an extensible enum backed by a string) and it is used as an exploded @query array parameter, the C# emitter generates code that calls .ToString() on each element and passes the result directly to AppendQuery(..., escape: true).

Because a default-constructed string-backed struct has a null internal string, .ToString() can return null. Uri.EscapeDataString (called internally by AppendQuery) throws an ArgumentNullException when passed a null string, causing a runtime crash.

Example of the generated code pattern that is unsafe:

foreach (var @param in include)
{
    uri.AppendQuery("include[]", @param.ToString(), true);
}

The emitter should guard against null/empty values before appending each exploded query parameter, for example:

foreach (var @param in include)
{
    if (!string.IsNullOrEmpty(@param.ToString()))
    {
        uri.AppendQuery("include[]", @param.ToString(), true);
    }
}

This was identified in openai/openai-dotnet#1255 where IncludedConversationItemProperty is a string-backed struct and the generated ConversationClient.RestClient.cs contained this pattern in multiple locations (lines 43, 62, and 103 of the generated file).

Reproduction

  1. Define a TypeSpec model with a string-backed extensible enum, e.g.:
@doc("An extensible string enum")
union MyStringEnum {
  string,
  valueA: "value-a",
  valueB: "value-b",
}
  1. Use it as an exploded array query parameter on an operation:
op myOp(
  @query(#{ explode: true }) include: MyStringEnum[],
): void;
  1. Emit the C# client with @typespec/http-client-csharp.
  2. Observe the generated code calls @param.ToString() without a null guard inside the foreach loop before passing to AppendQuery(..., escape: true).
  3. At runtime, constructing a default MyStringEnum value (null internal string) and including it in the include list causes Uri.EscapeDataString to throw ArgumentNullException.

Checklist

Metadata

Metadata

Labels

bugSomething isn't workingemitter:client:csharpIssue for the C# client emitter: @typespec/http-client-csharp

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions