From 91155c287a86503f597abbe54b215397ca77309f Mon Sep 17 00:00:00 2001 From: Brandon McAnsh Date: Mon, 29 Jun 2026 16:31:55 -0400 Subject: [PATCH] fix: silence ClosedSendChannelException during bidi stream reconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../getcode/opencode/internal/bidi/OpenStream.kt | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/services/opencode/src/main/kotlin/com/getcode/opencode/internal/bidi/OpenStream.kt b/services/opencode/src/main/kotlin/com/getcode/opencode/internal/bidi/OpenStream.kt index 21502e226..147a562e2 100644 --- a/services/opencode/src/main/kotlin/com/getcode/opencode/internal/bidi/OpenStream.kt +++ b/services/opencode/src/main/kotlin/com/getcode/opencode/internal/bidi/OpenStream.kt @@ -122,15 +122,23 @@ fun openBidirectionalStream( requestChannel.send(initialRequest()) trace(tag = tag, message = "Initial request sent") } catch (e: Exception) { + collectionJob.cancel() + requestChannel.close() + if (e is ClosedSendChannelException) { + // Expected race: the response flow can close the request + // channel (e.g. gRPC TRANSIENT_FAILURE) before the initial + // send completes. Retry silently. + trace(tag = tag, message = "Channel closed before initial request, retrying") + onReconnectAttempt?.invoke(attempt, e) + continue + } trace( tag = tag, message = "Failed to send initial request", type = TraceType.Error, error = e ) - collectionJob.cancel() - requestChannel.close() - if (isRetryable(e) || e is ClosedSendChannelException) { + if (isRetryable(e)) { onReconnectAttempt?.invoke(attempt, e) continue }