-
Notifications
You must be signed in to change notification settings - Fork 789
Description
Summary
The .NET SDK propagates exceptions thrown by event handlers, while the Node.js, Go, and Python SDKs all isolate exceptions per-handler. This behavioral inconsistency can lead to different application behavior depending on which SDK is used.
Current Behavior by SDK
| SDK | Exception Isolation | Error Reporting | Handler Storage |
|---|---|---|---|
| Node.js | try/catch per handler | Silent (caught, ignored) | Set / Map |
| Go | defer recover() per handler | fmt.Printf to stdout | slice with mutex |
| Python | try/except per handler | print() to stdout | set with lock |
| .NET | None — exceptions propagate | N/A (caller sees exception) | HashSet |
Code Evidence
Node.js — session.ts, _dispatchEvent():
for (const handler of typedHandlers) {
try {
handler(event as SessionEventPayload<typeof event.type>);
} catch (_error) {
// Handler error
}
}Go — session.go, dispatchEvent():
for _, handler := range handlers {
func() {
defer func() {
if r := recover(); r != nil {
fmt.Printf("Error in session event handler: %v\n", r)
}
}()
handler(event)
}()
}Python — session.py, _dispatch_event():
for handler in handlers:
try:
handler(event)
except Exception as e:
print(f"Error in session event handler: {e}").NET — Session.cs, DispatchEvent():
internal void DispatchEvent(SessionEvent sessionEvent)
{
foreach (var handler in _eventHandlers.ToArray())
{
handler(sessionEvent); // No try/catch — exceptions propagate
}
}Impact
-
Application resilience: In the .NET SDK, a single faulty handler can prevent all subsequent handlers from receiving events, potentially breaking SendAndWaitAsync or other internal mechanisms that depend on event dispatch.
-
Cross-SDK portability: Developers porting code between SDKs may encounter different failure modes. Code that works in Node.js/Go/Python (where one bad handler does not affect others) will behave differently in .NET.
-
Multiple handler registration: The issue is especially relevant when multiple independent components register handlers on the same session — a pattern encouraged by the IDisposable/Closeable return value from On().
Suggestion
Align the .NET SDK with the other three SDKs by wrapping each handler invocation in a try/catch:
internal void DispatchEvent(SessionEvent sessionEvent)
{
foreach (var handler in _eventHandlers.ToArray())
{
try
{
handler(sessionEvent);
}
catch (Exception ex)
{
// Log or report the error without breaking dispatch
Console.Error.WriteLine($"Error in session event handler: {ex}");
}
}
}Alternatively, if the .NET behavior is intentional, the other SDKs' silent swallowing could be reconsidered — but the majority (3 of 4) currently isolate, which seems like the safer default.
Environment
- Observed in the main branch of all four SDK implementations
- Discovered while building the community Java SDK (https://github.com/copilot-community-sdk/copilot-sdk-java)