Skip to content

Conversation

@midsbie
Copy link

@midsbie midsbie commented Nov 17, 2025

Fixes a race condition where Prompt() could return before all SessionUpdate notification handlers finished processing.

Notifications are handled asynchronously via goroutines, but responses are handled synchronously. This meant Prompt() would return as soon as the response arrived, even if notification handlers were still queued or running.

Now tracks in-flight notification handlers with a WaitGroup and waits for them to complete in SendRequest/SendRequestNoResult before returning. Ensures callers see all updates before the method returns.

Includes test that verifies all (10) SessionUpdate handlers complete before Prompt() returns; it previously failed with none completed.

Add a failing test that surfaces a race condition where Prompt() can return before all SessionUpdate
notification handlers have finished processing.

The issue occurs because:
- SessionUpdate notifications are handled asynchronously (goroutines)
- PromptResponse is handled synchronously
- The receive loop spawns notification handlers but doesn't track them

When a server sends multiple SessionUpdate notifications followed by a PromptResponse, the client's
Prompt() call returns immediately upon receiving the response, even though notification handlers may
still be queued or running.

The test expects all SessionUpdate handlers to complete before Prompt() returns, which represents
the intended semantic contract: a prompt operation includes all its updates. Currently fails with
0/10 handlers completed when Prompt() returns.

This will be fixed in a subsequent commit.
Fix race condition where Prompt() could return before all SessionUpdate notification handlers
finished processing.

The issue occurred because notification/request handlers were spawned asynchronously while responses
were processed synchronously. This meant the receive loop would:
1. Read SessionUpdate, then spawn goroutine G1
2. Read SessionUpdate, then spawn goroutine G2
3. Read PromptResponse, then handle synchronously, unblock Prompt()

At step 3, goroutines G1/G2 _could_ be queued, running, or complete.

Solution:
- Add notificationWg to Connection to track in-flight handlers
- Wrap notification handlers with WaitGroup Add/Done
- Call notificationWg.Wait() in SendRequest/SendRequestNoResult after receiving response but before
  returning to caller

This ensures the semantic contract that a prompt operation includes all its updates: when Prompt()
returns, all SessionUpdate notifications sent before the PromptResponse have been fully processed.

Fixes the test added in previous commit.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant