Repository navigation
Description
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.AspNetCore1.17.0-preview.260804.1 (the same code is in 1.19.0-preview.260822.1 and onmain)AGUI.Server/AGUI.Abstractions0.0.6Microsoft.Extensions.AI10.8.3- Client:
@ag-ui/client0.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
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
github-actions commentedon Aug 31, 2026
🤖 Automated triage reproduction notes (agent-authored — trust but verify)
Agent analysis
Repro:
dotnet/src/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore/ConfigureAGUIJsonOptions.cs::ConfigureAGUIJsonOptions.Configureline 24 registers the raw AGUI resolver; with AGUI.Abstractions 0.0.6, serializing aRunStartedEventthroughAddAGUIServer()JSON options emits explicit null optional fields. Minimal repro: override AGUI.Abstractions to 0.0.6, resolveJsonOptionsafterAddAGUIServer(), serialize a minimally populatedRunStartedEvent, and assert that:nullis absent; the test fails while 0.0.5 passes.dotnet/tests/Microsoft.Agents.AI.Hosting.AGUI.AspNetCore.UnitTests/ConfigureAGUIJsonOptionsTests.cs1.17.0-preview.260804.11.20.0nullfor optional event properties #8374 as a duplicate of this issuepetarnenov commentedon Oct 2, 2026
Still reproducible with
Microsoft.Agents.AI.Hosting.AGUI.AspNetCore1.23.0-preview.260928.1 withAGUI.*1.0.0, against@ag-ui/client1.0.1.ConfigureAGUIJsonOptionsstill appendsAGUIJsonSerializerContext.Default's resolver rather thanAGUIJsonUtilities.DefaultTypeInfoResolver. Details, and a related interrupt-serialization defect in the same method, are in #8962..NET: Fix AG-UI SSE events written with explicit nulls (microsoft#7920)