apache/pulsar · warning · org.apache.pulsar.client.impl.v5.PulsarClientException

Close interrupted

Error message

Close interrupted

What it means

ScalableQueueConsumer.close() waits on closeAsync().get(); if the waiting thread is interrupted, the interrupt flag is restored and a PulsarClientException with message "Close interrupted" is thrown. The close may still be in progress in the background — this error means the caller stopped waiting, not that close failed.

Source

Thrown at pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/ScalableQueueConsumer.java:266

        }
        var future = segmentConsumers.get(id.segmentId());
        if (future != null) {
            future.thenAccept(c -> c.negativeAcknowledge(id.v4MessageId()));
        }
    }

    @Override
    public AsyncQueueConsumer<T> async() {
        return asyncView;
    }

    @Override
    public void close() throws PulsarClientException {
        try {
            closeAsync().get();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new PulsarClientException("Close interrupted", e);
        } catch (ExecutionException e) {
            throw new PulsarClientException(e.getCause());
        }
    }

    // --- Async internals ---

    @Override
    public CompletableFuture<Message<T>> receiveAsync() {
        return receiveQueue.receiveAsync();
    }

    @Override
    public CompletableFuture<Void> closeAsync() {
        closed = true;
        receiveQueue.close();
        dagWatch.close();

View on GitHub (pinned to 820761864e)

Solutions

  1. Let shutdown complete before interrupting worker threads (order shutdown: consumers first, executors last)
  2. Catch PulsarClientException and check that the interrupt flag is set to distinguish this case
  3. Prefer closeAsync() with orTimeout() over interrupt-based timeouts
  4. After catching, re-check consumer state and call closeAsync() again if needed

Example fix

// before
executor.shutdownNow(); // interrupts in-flight close()
consumer.close();
// after
consumer.close();       // close consumers first
executor.shutdownNow(); // then interrupt workers
// or: consumer.closeAsync().get(30, TimeUnit.SECONDS);
Defensive patterns

Strategy: try-catch

Try / catch

try {
    consumer.close();
} catch (PulsarClientException e) {
    if ("Close interrupted".equals(e.getMessage())) {
        log.warn("close interrupted; consumer may still be closing");
        consumer.closeAsync(); // confirm/finish in background
    }
}

Prevention

When it happens

Trigger: The thread calling close() is interrupted while blocked in closeAsync().get() — typically shutdown hooks, executor shutdownNow(), or future.cancel(true) interrupting the closing thread.

Common situations: Graceful shutdown racing with ExecutorService.shutdownNow(); timeouts implemented via thread interruption; application kill/SIGTERM handlers interrupting consumer-close threads.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/a228e209c72c4cc0. Report an issue: GitHub.