The desktop app raises a first-chance System.IO.InvalidDataException for every UI broadcast it receives but does not handle, if the broadcast carries an argument.
Evidence (Debug build, macOS, 2026-09-19): dotnet-counters on the running app showed dotnet.exceptions[error.type=InvalidDataException] ticking; a temporary FirstChanceException hook printed the same stack four times in 45 s during ordinary session activity:
InvalidDataException: Invocation provides 1 argument(s) but target expects 0.
at Microsoft.AspNetCore.SignalR.Protocol.JsonHubProtocol.BindTypes(Utf8JsonReader& reader, IReadOnlyList`1 paramTypes)
Cause. The app connects to /hubs/sessions (ServerConnectionService) and the server puts it in the UI clients group, so it receives every Clients.UiClients().SendAsync(...) broadcast — PermissionPending(sessionId), PermissionResponded(sessionId, requestId), ActiveSessionAdded/Changed/Removed(sessionId), SessionTitleChanged(sessionId), SubagentAdopted(sessionId), SessionDeleted(sessionId), LaunchFailed(agentId, reason) and the rest of Broadcast/UiBroadcast.cs on the server. The app registers no handler for any of them (its only handlers are the Eventuous client's StreamEvent/StreamError). For a target with no handler, HubConnection.GetParameterTypes returns Type.EmptyTypes (Microsoft.AspNetCore.SignalR.Client.Core 10.0.12), so JsonHubProtocol.BindTypes throws on any argument; the client catches it as an InvocationBindingFailureMessage and drops the message. Payload-less nudges (AgentInstancesChanged, DaemonsChanged) bind cleanly and are dropped without an exception.
Impact. Functionally none today — the app consumes streams through Eventuous and ignores the nudges. The cost is one thrown-and-caught exception per unhandled broadcast (stack capture on the SignalR receive loop) plus a Debug-level "Failed to bind arguments" log line, at whatever rate hooks and session changes produce broadcasts across the tenant; it also makes InvalidDataException useless as a signal when profiling the app.
Options.
- Register handlers for the broadcasts the app can use (
ActiveSessionChanged, SessionTitleChanged, PermissionPending/PermissionResponded are all things the rail and chat could refresh on) and no-op handlers for the rest, with a test that every name in UiBroadcast.cs has a registration.
- Have the server not enrol a connection in the UI clients group unless it asks to (the app never needs the nudges while it tails streams).
- Leave it and document it as expected noise.
Found while profiling the glass material branch (#1035); unrelated to it.
The desktop app raises a first-chance
System.IO.InvalidDataExceptionfor every UI broadcast it receives but does not handle, if the broadcast carries an argument.Evidence (Debug build, macOS, 2026-09-19):
dotnet-counterson the running app showeddotnet.exceptions[error.type=InvalidDataException]ticking; a temporaryFirstChanceExceptionhook printed the same stack four times in 45 s during ordinary session activity:Cause. The app connects to
/hubs/sessions(ServerConnectionService) and the server puts it in the UI clients group, so it receives everyClients.UiClients().SendAsync(...)broadcast —PermissionPending(sessionId),PermissionResponded(sessionId, requestId),ActiveSessionAdded/Changed/Removed(sessionId),SessionTitleChanged(sessionId),SubagentAdopted(sessionId),SessionDeleted(sessionId),LaunchFailed(agentId, reason)and the rest ofBroadcast/UiBroadcast.cson the server. The app registers no handler for any of them (its only handlers are the Eventuous client'sStreamEvent/StreamError). For a target with no handler,HubConnection.GetParameterTypesreturnsType.EmptyTypes(Microsoft.AspNetCore.SignalR.Client.Core 10.0.12), soJsonHubProtocol.BindTypesthrows on any argument; the client catches it as anInvocationBindingFailureMessageand drops the message. Payload-less nudges (AgentInstancesChanged,DaemonsChanged) bind cleanly and are dropped without an exception.Impact. Functionally none today — the app consumes streams through Eventuous and ignores the nudges. The cost is one thrown-and-caught exception per unhandled broadcast (stack capture on the SignalR receive loop) plus a Debug-level "Failed to bind arguments" log line, at whatever rate hooks and session changes produce broadcasts across the tenant; it also makes
InvalidDataExceptionuseless as a signal when profiling the app.Options.
ActiveSessionChanged,SessionTitleChanged,PermissionPending/PermissionRespondedare all things the rail and chat could refresh on) and no-op handlers for the rest, with a test that every name inUiBroadcast.cshas a registration.Found while profiling the glass material branch (#1035); unrelated to it.