sendBlocking se queda colgado hasta que se agota el tiempo

Síntoma

Cambiaste una operación a modo bloqueante con BlockingMessageGateway, y ahora la corrutina nunca se reanuda en caso de fallo: se queda colgada hasta que lanza TimeoutCancellationException. Las operaciones exitosas funcionan bien; solo las fallidas se quedan colgadas. Un inicio de sesión o pago que debería devolver un error en su lugar simplemente se agota el tiempo.

Causa

sendBlocking se reanuda cuando llega un mensaje del tipo exacto que pediste que esperara. Si esperas el subtipo de éxito (por ejemplo, SuccessRetailerLoginResponse) y la operación falla, el fallo llega como un tipo diferente (ErrorRetailerLoginResponse) — que nunca coincide con lo que estás esperando. El gateway sigue esperando, y tu corrutina se queda colgada hasta que se activa el tiempo de espera.

La respuesta de fallo sí fue entregada. Simplemente no estabas escuchando por su tipo.

Solución

Espera el supertipo sellado, no un subtipo, y luego ramifica con when:

// INCORRECTO — se reanuda solo en éxito; cada fallo se convierte en un tiempo de espera
val r: SuccessRetailerLoginResponse = gateway.sendBlocking { clientSDK.sendLoginRequest() }

// CORRECTO — se reanuda en éxito O error
val r: RetailerLoginResponse = gateway.sendBlocking(timeoutMillis = 30000) {
    clientSDK.sendLoginRequest()
}
when (r) {
    is SuccessRetailerLoginResponse -> { /* ok */ }
    is ErrorRetailerLoginResponse   -> { /* manejar el fallo */ }
}

La misma regla se aplica a cada operación: espera RetailerPaymentResponse, RetailerReversalResponse, etc. — el supertipo — y ramifica después. El supertipo cubre todos los resultados, por lo que tanto éxito como fallo reanudan tu corrutina.

Recuerda que el pago tiene un tercer resultado, PartialRetailerPaymentResponse. Esperar RetailerPaymentResponse lo captura; esperar solo el subtipo de éxito perdería tanto el error como los casos parciales.

¿Sigue agotándose el tiempo después de corregir el tipo?

Si esperas el supertipo y todavía se agota el tiempo, realmente no está llegando una respuesta — verifica que el gateway reciba cada mensaje (gateway.onNewMessage(message) en tu interceptor), que tengas una sesión activa (consulta Ciclo de vida de sesión e inicio de sesión), y que el tiempo de espera sea suficiente para la operación. Siempre maneja TimeoutCancellationException sin importar qué — un tiempo de espera real es en sí mismo una condición a la que tu app debe reaccionar.

Relacionado

  • Cómo funciona el modelo de mensajes — la explicación completa del patrón de supertipo.
  • Ciclo de vida de sesión e inicio de sesión — una sesión faltante también produce ninguna respuesta.