{"record":{"id":"a0a936d824ca9c16","repo":"aeron-io/aeron","slug":"unexpected-interrupt-clientconductor","errorCode":null,"errorMessage":"unexpected interrupt","messagePattern":"unexpected interrupt","errorType":"exception","errorClass":"AeronException","httpStatus":null,"severity":"error","filePath":"aeron-client/src/main/java/io/aeron/ClientConductor.java","lineNumber":1843,"sourceCode":"            service(correlationId);\n\n            if (driverEventsAdapter.receivedCorrelationId() == correlationId)\n            {\n                stashedChannelByRegistrationId.remove(correlationId);\n                final RegistrationException ex = driverException;\n                if (null != ex)\n                {\n                    driverException = null;\n                    throw ex;\n                }\n\n                return;\n            }\n\n            if (Thread.currentThread().isInterrupted())\n            {\n                terminateConductor();\n                throw new AeronException(\"unexpected interrupt\");\n            }\n        }\n        while (deadlineNs - nanoClock.nanoTime() > 0);\n\n        throw new DriverTimeoutException(\"no response from MediaDriver within \" +\n            SystemUtil.formatDuration(driverTimeoutNs));\n    }\n\n    private int checkTimeouts(final long nowNs)\n    {\n        int workCount = 0;\n\n        if ((timeOfLastServiceNs + idleSleepDurationNs) - nowNs < 0)\n        {\n            checkServiceInterval(nowNs);\n            timeOfLastServiceNs = nowNs;\n\n            workCount += checkLiveness(nowNs);","sourceCodeStart":1825,"sourceCodeEnd":1861,"githubUrl":"https://github.com/aeron-io/aeron/blob/6d60124e15e35c11b49ba2e3c2c2858a09a18803/aeron-client/src/main/java/io/aeron/ClientConductor.java#L1825-L1861","documentation":"Thrown when the thread waiting for a reply from the Media Driver is interrupted unexpectedly. The conductor treats an interrupt during a synchronous await as fatal: it terminates the conductor and raises AeronException('unexpected interrupt'). Aeron uses interrupts as a shutdown signal, not a control mechanism.","triggerScenarios":"Another thread calls Thread.interrupt() (e.g. executor.shutdownNow(), Thread.stop-style teardown, or Future.cancel(true)) on the application thread blocked in awaitResponse for an addPublication/addSubscription/other correlated command.","commonSituations":"Calling blocking Aeron add APIs inside a task pool that is cancelled with shutdownNow(); test frameworks timing out and interrupting threads; container/JVM shutdown hooks interrupting worker threads.","solutions":["Avoid blocking Aeron calls on threads that get interrupted; use async (non-blocking) variants and poll for the future later","Shut down executors gracefully (awaitTermination) before interrupting threads that use Aeron","If shutdown is intended, catch the exception and treat conductor termination as expected","Move Aeron setup off request/interruptible paths into dedicated lifecycle threads"],"exampleFix":"// before\nFuture<?> f = pool.submit(() -> aeron.addPublication(channel, stream));\nf.cancel(true); // interrupts mid-await\n// after\nFuture<?> f = pool.submit(() -> aeron.addPublication(channel, stream));\npool.shutdown();\npool.awaitTermination(10, TimeUnit.SECONDS); // no interrupt mid-await","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { aeron.addPublication(ch, stream); } catch (AeronException e) { if (e.getMessage().contains(\"unexpected interrupt\")) { handleShutdown(); } else { throw e; } }","preventionTips":["Avoid shutdownNow()/cancel(true) on threads inside blocking Aeron calls","Use async add APIs and poll completion instead of blocking awaits","Await executor termination before JVM shutdown"],"tags":["aeron","interruption","threads","lifecycle"],"backgroundTag":"thread-interrupted","analyzedSha":"6d60124e15e35c11b49ba2e3c2c2858a09a18803","analyzedAt":"2026-09-12T11:17:07.683Z","contentChangedAt":"2026-09-12T11:17:07.683Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}