Skip to content

Commit 91155c2

Browse files
committed
fix: silence ClosedSendChannelException during bidi stream reconnect
When gRPC enters TRANSIENT_FAILURE during a reconnect attempt, the response flow closes the request channel before the initial send completes. This is an expected race condition — handle it silently instead of reporting it to Bugsnag as an error. Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent dee7842 commit 91155c2

1 file changed

Lines changed: 11 additions & 3 deletions

File tree

  • services/opencode/src/main/kotlin/com/getcode/opencode/internal/bidi

services/opencode/src/main/kotlin/com/getcode/opencode/internal/bidi/OpenStream.kt

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,23 @@ fun <Request, Response, StreamRef> openBidirectionalStream(
122122
requestChannel.send(initialRequest())
123123
trace(tag = tag, message = "Initial request sent")
124124
} catch (e: Exception) {
125+
collectionJob.cancel()
126+
requestChannel.close()
127+
if (e is ClosedSendChannelException) {
128+
// Expected race: the response flow can close the request
129+
// channel (e.g. gRPC TRANSIENT_FAILURE) before the initial
130+
// send completes. Retry silently.
131+
trace(tag = tag, message = "Channel closed before initial request, retrying")
132+
onReconnectAttempt?.invoke(attempt, e)
133+
continue
134+
}
125135
trace(
126136
tag = tag,
127137
message = "Failed to send initial request",
128138
type = TraceType.Error,
129139
error = e
130140
)
131-
collectionJob.cancel()
132-
requestChannel.close()
133-
if (isRetryable(e) || e is ClosedSendChannelException) {
141+
if (isRetryable(e)) {
134142
onReconnectAttempt?.invoke(attempt, e)
135143
continue
136144
}

0 commit comments

Comments
 (0)