Skip to content

.NET: [Bug]: AddAGUIServer writes explicit nulls on the AG-UI wire with AGUI 0.0.6 (rejected by @ag-ui/client) #7919

Description

@LoicDagnas

Description

AddAGUIServer() registers the AG-UI wire types through AGUIJsonSerializerContext.Default.Options.TypeInfoResolver. AGUI 0.0.6 moved the "omit a property that has no value" rule off that resolver. In 0.0.5 the rule came from DefaultIgnoreCondition on the context's own options; in 0.0.6 it lives on the new AGUIJsonUtilities.DefaultTypeInfoResolver, which applies it per property with a type-info modifier so it travels with the resolver into other JsonSerializerOptions.

The consequence, for an app on hosting 1.17–1.19 with AGUI.Server / AGUI.Abstractions raised to 0.0.6, is that every SSE event goes out with explicit nulls for its optional fields:

data: {"type":"RUN_STARTED","threadId":"…","runId":"…","parentRunId":null,"input":null,"timestamp":null,"rawEvent":null,"metadata":null}

The AG-UI TypeScript client (@ag-ui/client 0.0.59) declares those fields optional, not nullable, so it rejects the first event and the whole run fails client-side:

ZodError: [
  { "code": "invalid_type", "expected": "string", "received": "null", "path": ["parentRunId"] },
  { "code": "invalid_type", "expected": "object", "received": "null", "path": ["input"] },
  …
]

AGUIJsonUtilities' own doc comment names this exact failure — "would start writing "parentMessageId": null and similar — the exact wire divergence that receiving SDKs have had to be patched to tolerate" — and tells integrators to compose AGUIJsonUtilities.DefaultTypeInfoResolver rather than the context directly.

This is not visible in this repo's own CI today because dotnet/Directory.Packages.props pins the AGUI packages at 0.0.5. It becomes the default behavior the moment that pin moves to 0.0.6.

Suggested fix — in ConfigureAGUIJsonOptions.Configure:

 chain.Add(AgentAbstractionsJsonUtilities.DefaultOptions.TypeInfoResolver!);
-chain.Add(AGUIJsonSerializerContext.Default.Options.TypeInfoResolver!);
+chain.Add(AGUIJsonUtilities.DefaultTypeInfoResolver);

AGUIJsonUtilities.DefaultTypeInfoResolver is AGUIJsonSerializerContext.Default with the omit-empty modifier attached, so it resolves the same types and only changes what is written for properties with no value. It is new in 0.0.6, so this line and the package bump have to land together.

Code Sample

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAGUIServer();
builder.AddAIAgent("agent", (sp, name) => new ChatClientAgent(chatClient, new ChatClientAgentOptions { Name = name }));

var app = builder.Build();
app.MapAGUIServer("/ag-ui", app.Services.GetRequiredKeyedService<AIAgent>("agent"));
app.Run();

With AGUI.Server / AGUI.Abstractions raised to 0.0.6, curl -N -H 'Accept: text/event-stream' … on that endpoint shows "timestamp": null, "metadata": null, "parentRunId": null and "input": null on the events. With 0.0.5 the same fields are omitted.

Workaround for anyone hitting this before the fix lands — put the modified resolver first in the chain, after AddAGUIServer():

builder.Services.Configure<JsonOptions>(options =>
    options.SerializerOptions.TypeInfoResolverChain.Insert(0, AGUIJsonUtilities.DefaultTypeInfoResolver));

Error Messages / Stack Traces

Agent execution failed: ZodError: [
  { "code": "invalid_type", "expected": "number", "received": "null", "path": ["timestamp"], "message": "Expected number, received null" },
  { "code": "invalid_type", "expected": "object", "received": "null", "path": ["metadata"], "message": "Expected object, received null" },
  { "code": "invalid_type", "expected": "string", "received": "null", "path": ["parentRunId"], "message": "Expected string, received null" },
  { "code": "invalid_type", "expected": "object", "received": "null", "path": ["input"], "message": "Expected object, received null" }
]

Package Versions

  • Microsoft.Agents.AI.Hosting.AGUI.AspNetCore 1.17.0-preview.260804.1 (the same code is in 1.19.0-preview.260822.1 and on main)
  • AGUI.Server / AGUI.Abstractions 0.0.6
  • Microsoft.Extensions.AI 10.8.3
  • Client: @ag-ui/client 0.0.59

.NET Version

net10.0

Additional Context

Both current hosting previews (1.17, 1.19) declare AGUI 0.0.5, so ConfigureAGUIJsonOptions and the AGUI package version have to move together for the wire format to stay unchanged.

Metadata

Metadata

Assignees

No one assigned

    Labels

    .NETUsage: [Issues, PRs], Target: .NettriageUsage: [Issues], Target: All issues that still need to be triaged

    Type

    No type

    Projects

    Status
    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions