$ mvn -pl outbox -am test -Dtest=DualWriteProblemTest (NaiveBillingService: save the Invoice, then separately call kafka.send() -- no coordinator between the two. spring.kafka.bootstrap-servers points at localhost:19999, nothing listening there) 2026-10-04T00:52:56.429+05:30 ERROR 5271 --- [outbox] [ main] o.s.k.support.LoggingProducerListener : Exception thrown when sending a message with key='1' and payload='WIDGET-1:1999' to topic invoices-naive: org.apache.kafka.common.errors.TimeoutException: Topic invoices-naive not present in metadata after 2000 ms. Exception thrown synchronously from issueInvoiceTheNaiveWay(): org.springframework.kafka.KafkaException: Send failed Invoice count regardless: 1 (was 0). The database write already committed inside saveInvoiceOnly() before this exception was ever thrown -- the exception came from the Kafka call, several lines later, on an already-committed row. 2026-10-04T00:52:58.495+05:30 ERROR 5271 --- [outbox] [ main] o.s.k.support.LoggingProducerListener : Exception thrown when sending a message with key='2' and payload='WIDGET-1:2999' to topic invoices-naive: org.apache.kafka.common.errors.TimeoutException: Topic invoices-naive not present in metadata after 2000 ms. Invoice count after the blocked, failed send: 2 (was 1). The row committed before the send was even attempted. [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0 Two real findings here, not one. First, the obvious one: the invoice exists in the database in both tests, and Kafka never received either message -- that's the dual-write problem, reproduced with nothing faked beyond pointing Kafka at a port with no listener. Second, a more specific one that contradicts the usual "fire-and-forget send() fails silently" framing: when the broker can't be reached to fetch topic metadata at all, Spring's KafkaTemplate.send() throws synchronously, wrapping the Kafka client's own TimeoutException in a KafkaException, even though the method's declared return type is a CompletableFuture. The "silent" version of this bug is real, but it is a narrower case than total broker unavailability -- it's what happens when the broker is reachable and the topic's metadata is known, but the specific produce request times out waiting for an acknowledgment afterward. That failure mode surfaces only through the returned future or a callback, never as a synchronous exception, and is not reproduced in this transcript (it needs a broker that accepts the connection and then stops responding mid-request, not one that was never there to begin with).