{"record":{"id":"9c50ec1f20197cf1","repo":"aeron-io/aeron","slug":"unexpected-close-of-heartbeat-timestamp-counter-counterid","errorCode":null,"errorMessage":"unexpected close of heartbeat timestamp counter: <counterId>","messagePattern":"unexpected close of heartbeat timestamp counter: <counterId>","errorType":"exception","errorClass":"AeronException","httpStatus":null,"severity":"error","filePath":"aeron-client/src/main/java/io/aeron/ClientConductor.java","lineNumber":1923,"sourceCode":"                    countersReader, HEARTBEAT_TYPE_ID, ctx.clientId());\n\n                if (NULL_COUNTER_ID != counterId)\n                {\n                    try\n                    {\n                        heartbeatTimestamp = new AtomicCounter(counterValuesBuffer, counterId);\n                        heartbeatTimestamp.setRelease(nowMs);\n                        appendToLabel(\n                            countersReader.metaDataBuffer(),\n                            counterId,\n                            \" name=\" + ctx.clientName() + \" \" +\n                            formatVersionInfo(AeronVersion.VERSION, AeronVersion.GIT_SHA));\n                        timeOfLastKeepAliveNs = nowNs;\n                    }\n                    catch (final RuntimeException ex)  // a race caused by the driver timing out the client\n                    {\n                        terminateConductor();\n                        throw new AeronException(\"unexpected close of heartbeat timestamp counter: \" + counterId, ex);\n                    }\n                }\n            }\n            else\n            {\n                final int counterId = heartbeatTimestamp.id();\n                if (!HeartbeatTimestamp.isActive(countersReader, counterId, HEARTBEAT_TYPE_ID, ctx.clientId()))\n                {\n                    terminateConductor();\n                    throw new AeronException(\"unexpected close of heartbeat timestamp counter: \" + counterId);\n                }\n\n                heartbeatTimestamp.setRelease(nowMs);\n                timeOfLastKeepAliveNs = nowNs;\n            }\n\n            return 1;\n        }","sourceCodeStart":1905,"sourceCodeEnd":1941,"githubUrl":"https://github.com/aeron-io/aeron/blob/6d60124e15e35c11b49ba2e3c2c2858a09a18803/aeron-client/src/main/java/io/aeron/ClientConductor.java#L1905-L1941","documentation":"AeronException thrown when the client attempts to update its heartbeat timestamp counter but the underlying counter has been closed (freed by the driver timing the client out). This is a race between driver-side client timeout and client-side keepalive; the conductor terminates since the driver no longer considers this client live.","triggerScenarios":"The driver timed out this client (e.g. because its conductor thread was starved) and freed the heartbeat counter; the client's next keepalive pass hits the closed counter and wraps the resulting exception.","commonSituations":"Client thread stalls long enough for the driver to expire the client, then resumes and races keepalive; paused VM/container resuming after driver timeout; debugging sessions freezing the client past the driver timeout.","solutions":["Treat the client as terminated and create a new Aeron instance (this is by design after driver eviction)","Keep the conductor serviced promptly: avoid blocking callbacks and long pauses (see interServiceTimeout)","Increase driver-side and client-side timeouts so marginal pauses do not evict the client","Inspect getCause() to confirm the counter-free race if diagnostics are needed"],"exampleFix":"// before\nAeron aeron = Aeron.connect(ctx);\n// long blocking work on the same thread ...\n// after\nAeron aeron = Aeron.connect(ctx);\nrunBlockingWorkOnSeparateExecutor(); // conductor stays serviced, client not evicted","handlingStrategy":"fallback","validationCode":"// Before heavy work that may stall the conductor:\nif (System.nanoTime() - lastServiceNs > interServiceTimeoutNs / 2) { serviceConductorNow(); }","typeGuard":null,"tryCatchPattern":"catch (AeronException e) { if (e.getMessage().startsWith(\"unexpected close of heartbeat timestamp counter\")) { client.close(); client = Aeron.connect(ctx); } } // driver evicted this client; reconnect","preventionTips":["Keep the conductor thread responsive; offload blocking work","Set driver/client timeouts comfortably above worst-case pauses","After driver eviction always create a new Aeron client rather than reusing the old one"],"tags":["aeron","driver","keepalive","race-condition"],"backgroundTag":"invalid-state-transition","analyzedSha":"6d60124e15e35c11b49ba2e3c2c2858a09a18803","analyzedAt":"2026-09-12T11:17:07.683Z","contentChangedAt":"2026-09-12T11:17:07.683Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}