Skip to content

fix the race condition for remote stream close on context cancel - #3591

Merged
akshaydeo merged 1 commit into
devfrom
05-19-fix_the_race_condition_for_remote_stream_close_on_context_cancel
May 19, 2026
Merged

fix the race condition for remote stream close on context cancel#3591
akshaydeo merged 1 commit into
devfrom
05-19-fix_the_race_condition_for_remote_stream_close_on_context_cancel

Conversation

@akshaydeo

@akshaydeo akshaydeo commented May 19, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes a nil-dereference panic in fasthttp's connsCleaner that occurred when a mid-stream client disconnect caused LargeResponseReader.Close to call ReleaseResponse on a connection that had already been torn down by SetupStreamCancellation. The fix prevents the double-release by checking BifrostContextKeyConnectionClosed before draining and releasing the fasthttp response.

Changes

  • LargeResponseReader now holds a *schemas.BifrostContext reference so that Close() can inspect BifrostContextKeyConnectionClosed before attempting to drain and release the underlying fasthttp response. When the flag is set (indicating the connection was already closed mid-stream), Close() skips the drain and release, leaking r.Resp to the GC instead — mirroring the existing behavior of ReleaseStreamingResponse.
  • SetupStreamCancellation is now wired into SetupStreamingPassthrough so that mid-stream client disconnects unblock the transport's Read via CloseWithError and set BifrostContextKeyConnectionClosed before LargeResponseReader.Close runs.
  • In SetupStreamCancellation, BifrostContextKeyConnectionClosed is now set unconditionally after a close attempt in the done+cancelled-context race branch, regardless of whether the close returned an error. Previously the flag was only set on a successful close, meaning a failed close (e.g. against an already-pooled conn) left the flag unset and allowed a second release to proceed.

Type of change

  • Bug fix

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • UI (React)

How to test

Simulate a mid-stream client disconnect against a streaming endpoint and confirm the server does not panic with a nil-dereference in fasthttp's connsCleaner. Verify that normal stream completion (EOF) still correctly releases the response.

go version
go test ./...

Breaking changes

  • No

Security considerations

None. This change only affects connection lifecycle management for streaming responses and does not touch auth, secrets, or PII handling.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

@coderabbitai

coderabbitai Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Rate limit exceeded

@akshaydeo has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 1 minute and 59 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: de453af9-0008-4e21-9647-2465176bd5ef

📥 Commits

Reviewing files that changed from the base of the PR and between 32edcc3 and d888280.

📒 Files selected for processing (6)
  • core/providers/utils/largeresponse.go
  • core/providers/utils/makerequest_test.go
  • core/providers/utils/streamingclient_test.go
  • core/providers/utils/utils.go
  • core/providers/utils/utilsjson_test.go
  • ui/app/workspace/config/feature-flags/layout.tsx
📝 Walkthrough

Walkthrough

This PR addresses double-release of fasthttp responses in streaming scenarios by adding context-aware connection-state tracking to LargeResponseReader, wiring that context through both large-response detection paths, and ensuring SetupStreamCancellation consistently signals connection closure to prevent subsequent release attempts.

Changes

Streaming Response Double-Release Prevention

Layer / File(s) Summary
LargeResponseReader context tracking and early-close logic
core/providers/utils/large_response.go
LargeResponseReader gains a ctx *schemas.BifrostContext field and Close() adds early return: when BifrostContextKeyConnectionClosed is set, runs cleanup, nils Resp, and skips fasthttp.ReleaseResponse to avoid double-release.
Context wiring through large-response detection paths
core/providers/utils/large_response.go
Both unknown-Content-Length threshold-based buffering (line 200) and known-large-response prefetch+stream (line 261) paths now instantiate LargeResponseReader with ctx: ctx.
Cancellation lifecycle and race-condition handling
core/providers/utils/large_response.go, core/providers/utils/utils.go
SetupStreamingPassthrough wires SetupStreamCancellation to trigger on mid-stream client disconnect and updates reader cleanup; SetupStreamCancellation's done-path race handling now unconditionally marks BifrostContextKeyConnectionClosed after close attempt to prevent a second CloseWithError during release.

Minor Formatting

Layer / File(s) Summary
UI configuration whitespace adjustment
ui/app/workspace/config/feature-flags/layout.tsx
Reformatted createFileRoute options object indentation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • maximhq/bifrost#3541: Both PRs adjust stream/cancellation lifecycle handling to avoid double-closing/canceling during streaming (main PR updates SetupStreamCancellation/LargeResponseReader to track ctx and prevent double release, while #3541 scopes cancel() ownership in GenericRouter.createHandler for streaming).
  • maximhq/bifrost#3582: Both PRs adjust stream-cancellation teardown in core/providers/utils/utils.go around BifrostContextKeyConnectionClosed, ensuring close/release logic is skipped or updated consistently when the connection/body is already closed.
  • maximhq/bifrost#3529: Both PRs modify the streaming cancellation handling in core/providers/utils/utils.go (main PR adjusts SetupStreamCancellation's context-cancel/connection-closed behavior; retrieved PR changes HandleStreamCancellation's construction of the 499 status code).

Suggested reviewers

  • danpiths

Poem

🐰 A rabbit hops through streaming code,
Where cancels roam and connections unload,
With context flags and cleanup care,
No double-release ghosts to scare—
The context closes, the reader sleeps,
And fasthttp its promise keeps! 🏃‍♂️✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The linked issue #123 addresses File API support for providers, which is completely unrelated to the code changes fixing a race condition in streaming response handling. There is a significant mismatch between the PR objectives and the linked issue requirements. Either link the correct issue related to race condition fixes in streaming responses, or remove the unrelated File API issue if this PR does not address it.
Out of Scope Changes check ⚠️ Warning The PR's code changes (LargeResponseReader context handling, SetupStreamCancellation logic, and a whitespace formatting change) address the stated race condition in streaming responses, which is consistent with the PR title and description; however, the linked issue #123 about File API support is unrelated, indicating potential scope confusion. Clarify the link between this PR and issue #123, or update the linked issues to reflect the actual problem being fixed (race condition in stream closure).
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix the race condition for remote stream close on context cancel' clearly and specifically summarizes the main change—addressing a race condition in streaming response handling during context cancellation.
Description check ✅ Passed The PR description is substantially complete with Summary, Changes, Type of change, Affected areas, How to test, Breaking changes, Security considerations, and Checklist sections properly filled out.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 05-19-fix_the_race_condition_for_remote_stream_close_on_context_cancel

Comment @coderabbitai help to get the list of available commands and usage tips.

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@akshaydeo
akshaydeo marked this pull request as ready for review May 19, 2026 11:30
@coderabbitai
coderabbitai Bot requested a review from danpiths May 19, 2026 11:30

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@core/providers/utils/large_response.go`:
- Around line 48-58: The early-return guard that checks
r.ctx.Value(schemas.BifrostContextKeyConnectionClosed) must be re-evaluated
immediately after calling r.cleanup() inside Close() (and the similar
drain+ReleaseResponse locations) because cleanup() can call stopCancellation()
which may set that context key; change the sequence in Close() (and the other
spots referenced) so that after invoking r.cleanup() you re-check
r.ctx.Value(schemas.BifrostContextKeyConnectionClosed) and if it is true skip
calling fasthttp.ReleaseResponse(r.Resp), set r.Resp = nil and return to avoid a
double-close/race.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: db97ff45-664d-4491-ac64-be4d0d009b5e

📥 Commits

Reviewing files that changed from the base of the PR and between 3a33800 and 32edcc3.

📒 Files selected for processing (3)
  • core/providers/utils/large_response.go
  • core/providers/utils/utils.go
  • ui/app/workspace/config/feature-flags/layout.tsx

Comment thread core/providers/utils/large_response.go Outdated
@greptile-apps

greptile-apps Bot commented May 19, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

The fix is safe to merge; the reordering of cleanup before the connection-closed flag check is the correct sequencing to guarantee the cancellation goroutine has fully settled before LargeResponseReader.Close() decides whether to call fasthttp.ReleaseResponse.

The race being addressed is narrow and well-understood: the cleanup closure synchronously waits for the cancellation goroutine (<-closed) before returning, and only then is the BifrostContextKeyConnectionClosed flag read. The done-branch change (unconditional flag set on close attempt) mirrors the existing ctx.Done() branch and correctly handles the failed-close-against-already-pooled-conn scenario. The three file renames and the UI whitespace change are inert.

The cleanup field in LargeResponseReader.Close() is still not guarded against concurrent callers — two goroutines racing to call Close() can both see r.cleanup != nil and the second close(done) will panic — but this concern was already flagged in a prior review round.

Important Files Changed

Filename Overview
core/providers/utils/largeresponse.go Core fix: adds ctx field to LargeResponseReader, reorders cleanup before connection-closed flag check in Close(), and wires SetupStreamCancellation into SetupStreamingPassthrough. Logic is sound for the race being fixed; the FinalizeResponseWithLargeDetection code paths get ctx but no SetupStreamCancellation, which is intentional since they lack the goroutine-based race.
core/providers/utils/utils.go In the done-branch of SetupStreamCancellation, BifrostContextKeyConnectionClosed is now set unconditionally after a close attempt (regardless of error), matching the existing ctx.Done() branch and preventing a second release against an already-pooled conn.
core/providers/utils/makerequest_test.go Renamed from make_request_test.go — no content changes.
core/providers/utils/streamingclient_test.go Renamed from streaming_client_test.go — no content changes.
core/providers/utils/utilsjson_test.go Renamed from utils_json_test.go — no content changes.
ui/app/workspace/config/feature-flags/layout.tsx Indentation-only change (tab → spaces) — no functional impact.

Reviews (2): Last reviewed commit: "fix the race condition for remote stream..." | Re-trigger Greptile

Comment thread core/providers/utils/utils.go
@akshaydeo
akshaydeo force-pushed the 05-19-fix_the_race_condition_for_remote_stream_close_on_context_cancel branch from 32edcc3 to d888280 Compare May 19, 2026 12:14

akshaydeo commented May 19, 2026

Copy link
Copy Markdown
Contributor Author

Merge activity

  • May 19, 12:19 PM UTC: A user started a stack merge that includes this pull request via Graphite.
  • May 19, 12:19 PM UTC: @akshaydeo merged this pull request with Graphite.

@akshaydeo
akshaydeo merged commit d3d9f21 into dev May 19, 2026
15 of 16 checks passed
@akshaydeo
akshaydeo deleted the 05-19-fix_the_race_condition_for_remote_stream_close_on_context_cancel branch May 19, 2026 12:19
This was referenced May 19, 2026
akshaydeo added a commit that referenced this pull request May 20, 2026
## Summary

Fixes a nil-dereference panic in fasthttp's `connsCleaner` that occurred when a mid-stream client disconnect caused `LargeResponseReader.Close` to call `ReleaseResponse` on a connection that had already been torn down by `SetupStreamCancellation`. The fix prevents the double-release by checking `BifrostContextKeyConnectionClosed` before draining and releasing the fasthttp response.

## Changes

- `LargeResponseReader` now holds a `*schemas.BifrostContext` reference so that `Close()` can inspect `BifrostContextKeyConnectionClosed` before attempting to drain and release the underlying fasthttp response. When the flag is set (indicating the connection was already closed mid-stream), `Close()` skips the drain and release, leaking `r.Resp` to the GC instead — mirroring the existing behavior of `ReleaseStreamingResponse`.
- `SetupStreamCancellation` is now wired into `SetupStreamingPassthrough` so that mid-stream client disconnects unblock the transport's `Read` via `CloseWithError` and set `BifrostContextKeyConnectionClosed` before `LargeResponseReader.Close` runs.
- In `SetupStreamCancellation`, `BifrostContextKeyConnectionClosed` is now set unconditionally after a close attempt in the `done`+cancelled-context race branch, regardless of whether the close returned an error. Previously the flag was only set on a successful close, meaning a failed close (e.g. against an already-pooled conn) left the flag unset and allowed a second release to proceed.

## Type of change

- [x] Bug fix

## Affected areas

- [x] Core (Go)
- [x] Transports (HTTP)
- [x] UI (React)

## How to test

Simulate a mid-stream client disconnect against a streaming endpoint and confirm the server does not panic with a nil-dereference in fasthttp's `connsCleaner`. Verify that normal stream completion (EOF) still correctly releases the response.

```sh
go version
go test ./...
```

## Breaking changes

- [x] No

## Security considerations

None. This change only affects connection lifecycle management for streaming responses and does not touch auth, secrets, or PII handling.

## Checklist

- [ ] I read `docs/contributing/README.md` and followed the guidelines
- [ ] I added/updated tests where appropriate
- [ ] I updated documentation where needed
- [ ] I verified builds succeed (Go and UI)
- [ ] I verified the CI pipeline passes locally if applicable
@akshaydeo akshaydeo mentioned this pull request May 20, 2026
@coderabbitai coderabbitai Bot mentioned this pull request Jul 6, 2026
17 tasks
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