{"record":{"id":"87c992aea442d19c","repo":"apache/pulsar","slug":"close-interrupted-87c992","errorCode":null,"errorMessage":"Close interrupted","messagePattern":"Close interrupted","errorType":"exception","errorClass":"org.apache.pulsar.client.impl.v5.PulsarClientException","httpStatus":null,"severity":"error","filePath":"pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/MultiTopicStreamConsumer.java","lineNumber":359,"sourceCode":"                // detached it and no longer ack removed topics, so skip its slice.\n                continue;\n            }\n            action.accept(state.consumer, entry.getValue());\n        }\n    }\n\n    @Override\n    public AsyncStreamConsumer<T> async() {\n        return asyncView;\n    }\n\n    @Override\n    public void close() throws PulsarClientException {\n        try {\n            closeAsync().get();\n        } catch (InterruptedException e) {\n            Thread.currentThread().interrupt();\n            throw new PulsarClientException(\"Close interrupted\", e);\n        } catch (ExecutionException e) {\n            throw new PulsarClientException(e.getCause());\n        }\n    }\n\n    CompletableFuture<Void> closeAsync() {\n        if (closed) {\n            return CompletableFuture.completedFuture(null);\n        }\n        closed = true;\n        watcher.close();\n        mux.close();\n        // Cancel pending retries for topics that never finished subscribing (they're not in\n        // perTopic, so the closeTopic loop below wouldn't reach them).\n        retryTimeouts.values().forEach(Timeout::cancel);\n        retryTimeouts.clear();\n        List<CompletableFuture<Void>> closes = new ArrayList<>();\n        for (var topic : new HashSet<>(perTopic.keySet())) {","sourceCodeStart":341,"sourceCodeEnd":377,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/MultiTopicStreamConsumer.java#L341-L377","documentation":"MultiTopicStreamConsumer.close was interrupted while waiting for the async close to complete; the interrupt is re-set on the thread and surfaced as a PulsarClientException so callers know close did not finish normally.","triggerScenarios":"Thread interruption during close(), typically from executor shutdownNow(), task cancellation, or shutdown hooks.","commonSituations":"App teardown racing consumer close; watchdog threads interrupting slow closes during broker unavailability.","solutions":["Avoid interrupting the closing thread; use closeAsync() for non-blocking close","Retry close if resources remain after the interrupt"],"exampleFix":"// before\nstreamConsumer.close(); // blocking, interruptible\n// after\nstreamConsumer.closeAsync()\n    .get(30, TimeUnit.SECONDS); // or handle InterruptedException explicitly","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { streamConsumer.close(); } catch (PulsarClientException e) { /* check interruption state */ }","preventionTips":["Prefer closeAsync().orTimeout(...) in shutdown paths","Complete consumer close before interrupting threads"],"tags":["pulsar","client","close","interrupt"],"backgroundTag":"close-interrupted","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}