diff --git a/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/data/remote/GrpcClient.kt b/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/data/remote/GrpcClient.kt index 86da13d5..52b374ba 100644 --- a/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/data/remote/GrpcClient.kt +++ b/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/data/remote/GrpcClient.kt @@ -790,15 +790,16 @@ class GrpcClient @Inject constructor() { override fun onError(t: Throwable) { Log.e(TAG, "Message stream error: ${t.message}") - // Ignore events from stale streams + // Ignore events from stale streams - close without exception to avoid crash if (messageStreamVersion.get() != streamVersion) { Log.d(TAG, "Ignoring error from stale message stream") - close(t) + close() return } - // Don't trigger reconnect for CANCELLED errors - if (!t.message.orEmpty().contains("CANCELLED")) { + // Don't trigger reconnect for CANCELLED or channel shutdown errors + val errorMessage = t.message.orEmpty() + if (!errorMessage.contains("CANCELLED") && !errorMessage.contains("shutdownNow")) { triggerReconnect("Message stream error: ${t.message}") } close(t) @@ -870,15 +871,16 @@ class GrpcClient @Inject constructor() { override fun onError(t: Throwable) { Log.e(TAG, "Session event stream error: ${t.message}") - // Ignore events from stale streams + // Ignore events from stale streams - close without exception to avoid crash if (eventStreamVersion.get() != streamVersion) { Log.d(TAG, "Ignoring error from stale event stream") - close(t) + close() return } - // Don't trigger reconnect for CANCELLED errors - if (!t.message.orEmpty().contains("CANCELLED")) { + // Don't trigger reconnect for CANCELLED or channel shutdown errors + val errorMessage = t.message.orEmpty() + if (!errorMessage.contains("CANCELLED") && !errorMessage.contains("shutdownNow")) { triggerReconnect("Event stream error: ${t.message}") } close(t) diff --git a/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/data/repository/TssRepository.kt b/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/data/repository/TssRepository.kt index 4cedbdca..98e62d39 100644 --- a/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/data/repository/TssRepository.kt +++ b/backend/mpc-system/services/service-party-android/app/src/main/java/com/durian/tssparty/data/repository/TssRepository.kt @@ -284,7 +284,12 @@ class TssRepository @Inject constructor( currentSessionEventPartyId = effectivePartyId android.util.Log.d("TssRepository", "Starting session event subscription for partyId: $effectivePartyId (device partyId: $partyId)") sessionEventJob = repositoryScope.launch { - grpcClient.subscribeSessionEvents(effectivePartyId).collect { event -> + grpcClient.subscribeSessionEvents(effectivePartyId) + .catch { e -> + // Log error but don't crash - connection will be restored by GrpcClient + android.util.Log.e("TssRepository", "Session event stream error: ${e.message}") + } + .collect { event -> android.util.Log.d("TssRepository", "=== Session event received ===") android.util.Log.d("TssRepository", " eventType: ${event.eventType}") android.util.Log.d("TssRepository", " sessionId: ${event.sessionId}") @@ -1859,7 +1864,12 @@ class TssRepository @Inject constructor( // Collect incoming messages from gRPC and send to TSS launch { - grpcClient.subscribeMessages(sessionId, effectivePartyId).collect { message -> + grpcClient.subscribeMessages(sessionId, effectivePartyId) + .catch { e -> + // Log error but don't crash - connection will be restored by GrpcClient + android.util.Log.e("TssRepository", "Message stream error: ${e.message}") + } + .collect { message -> // Find party index from party ID val session = _currentSession.value val fromPartyIndex = session?.participants?.find { it.partyId == message.fromParty }?.partyIndex