{"id":"39b9593f4e5e3910","repo":"apache/kafka","slug":"received-interrupt-while-awaiting-operation","errorCode":null,"errorMessage":"Received interrupt while awaiting {operation}","messagePattern":"Received interrupt while awaiting (.+?)","errorType":"exception","errorClass":"InterruptException","httpStatus":null,"severity":"warning","filePath":"clients/src/main/java/org/apache/kafka/clients/producer/internals/TransactionalRequestResult.java","lineNumber":63,"sourceCode":"\n    public void done() {\n        this.latch.countDown();\n    }\n\n    public void await(long timeout, TimeUnit unit, String expectedTimeoutReason) {\n        try {\n            boolean success = latch.await(timeout, unit);\n            if (!success) {\n                throw new TimeoutException(\"Timeout expired after \" + unit.toMillis(timeout) +\n                    \"ms while awaiting \" + operation + \". \" + expectedTimeoutReason);\n            }\n\n            isAcked = true;\n            if (error != null) {\n                throw error;\n            }\n        } catch (InterruptedException e) {\n            throw new InterruptException(\"Received interrupt while awaiting \" + operation, e);\n        }\n    }\n\n    public RuntimeException error() {\n        return error;\n    }\n\n    public boolean isSuccessful() {\n        return isCompleted() && error == null;\n    }\n\n    public boolean isCompleted() {\n        return latch.getCount() == 0L;\n    }\n\n    public boolean isAcked() {\n        return isAcked;\n    }","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/producer/internals/TransactionalRequestResult.java#L45-L81","documentation":"Wrapped as InterruptException (a KafkaException subtype) and thrown from TransactionalRequestResult.await when the thread blocking on a transactional control RPC's CountDownLatch is interrupted via Thread.interrupt() or a shutdown hook. The interrupt is honoured: the await aborts and the producer surfaces it instead of swallowing the status. Because the underlying RPC's outcome is unknown (it may still complete on the broker), the producer's transactional state should be treated as potentially pending; further transactional calls may hit the pending-transition checks.","triggerScenarios":"A thread calling initTransactions/beginTransaction/commitTransaction/abortTransaction/sendOffsetsToTransaction is interrupted while blocked on the internal latch — e.g. JVM shutdown hook fires, an ExecutorService.shutdownNow() interrupts the worker, or application code explicitly calls Thread.interrupt() on the producer thread.","commonSituations":"Container/pod shutdown signal (SIGTERM) triggering a shutdown hook that interrupts producer threads; ExecutorService.shutdownNow() during application teardown; framework timeout (e.g. Spring @Transactional, Lambda deadline) interrupting the worker; explicit cancel of a Future wrapping producer calls; graceful-shutdown logic racing with an in-flight commit.","solutions":["Make shutdown cooperative: drain/close the producer with KafkaProducer.close(Duration) before interrupting worker threads, and don't call shutdownNow() while a transactional operation is in flight.","If interrupted mid-transaction, treat the producer as poisoned: abort or close and recreate; do not assume the transaction completed.","Ensure producer work runs on a thread whose lifecycle is owned by the producer's close path, so interrupts don't hit active transactions.","If using a reactive/task framework, configure it to wait for the producer future instead of cancelling/interrupting."],"exampleFix":"// before\nExecutorService exec = Executors.newSingleThreadExecutor();\nFuture<?> f = exec.submit(() -> {\n    producer.commitTransaction();  // await() blocks\n});\nexec.shutdownNow();  // interrupts the worker -> InterruptException 296\n\n// after\nf.get(30, TimeUnit.SECONDS);  // wait for the commit to finish\nexec.shutdown();\nexec.awaitTermination(10, TimeUnit.SECONDS);\nproducer.close(Duration.ofSeconds(10));","handlingStrategy":"try-catch","validationCode":"// Pre-check the interrupt status before issuing a transactional call so a prior\n// interrupt does not surface mid-await:\nif (Thread.interrupted()) {\n    throw new InterruptedException(\"Refusing transactional call on an interrupted thread\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    producer.commitTransaction();\n} catch (org.apache.kafka.common.errors.InterruptException e) {\n    // The await was interrupted. Restore the status, decide on shutdown vs retry:\n    Thread.currentThread().interrupt();\n    if (shuttingDown) { producer.close(Duration.ZERO); throw e; }\n    // Otherwise retry — transactional control calls are idempotent.\n    producer.commitTransaction();\n}","preventionTips":["Never call KafkaProducer transactional methods from threads you do not own (e.g. shared thread pools that interrupt on shutdown).","Clear or honor Thread.interrupted() deliberately before each transactional call; do not let stray interrupts propagate through awaits.","On graceful shutdown, call producer.close(Duration) instead of interrupting the producer thread, so you get clean transaction completion."],"tags":["transactions","interrupt","shutdown","threading"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}