diff --git a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/sync/CustomWebMvcSseServerTransportProvider.java b/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/sync/CustomWebMvcSseServerTransportProvider.java index 3442344d..a798d2cf 100644 --- a/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/sync/CustomWebMvcSseServerTransportProvider.java +++ b/dap-gateway/src/main/java/io/shinhanlife/dap/mcg/sync/CustomWebMvcSseServerTransportProvider.java @@ -224,7 +224,8 @@ public class CustomWebMvcSseServerTransportProvider implements McpServerTranspor } } catch (Exception e) { if (e.getClass().getSimpleName().contains("AsyncRequestNotUsableException") || - (e.getCause() != null && e.getCause() instanceof java.io.IOException)) { + (e.getCause() != null && e.getCause() instanceof java.io.IOException) || + (e instanceof IllegalStateException && e.getMessage() != null && e.getMessage().contains("already completed"))) { log.debug("SSE emitter is disconnected (expected in Warm Pool): {}", e.getMessage()); } else { log.error("Error sending message to SSE emitter", e); @@ -235,6 +236,10 @@ public class CustomWebMvcSseServerTransportProvider implements McpServerTranspor this.emitter.completeWithError(e); } catch (Exception ignored) { // ignore already completed + } finally { + // 죽은 Emitter를 null로 초기화하여 다음 5초 주기 때 재시도하지 않도록 방지 + // 클라이언트가 재연결하면 setEmitter(새 Emitter)를 통해 덮어씌워짐 + this.emitter = null; } } }