Skip to content

Conversation

@midsbie
Copy link
Contributor

@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.
Copy link
Member

@ThomasK33 ThomasK33 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat. Thanks a lot 👍

@ThomasK33 ThomasK33 added this pull request to the merge queue Dec 5, 2025
Merged via the queue into coder:main with commit 48085b0 Dec 5, 2025
1 check passed
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.

2 participants