Skip to content

Event Handler Exception Propagation Inconsistency Across SDKs #388

@brunoborges

Description

@brunoborges

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

  1. 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.

  2. 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.

  3. 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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions