When a Message-Driven Bean fails to process a message (throws a system exception or calls setRollbackOnly()), the transaction rolls back and the JMS destination doesn’t receive an acknowledgment. The destination then retransmits the same message, causing the MDB to fail again — creating an infinite loop that wastes CPU and memory.
A poison message is a message that continuously fails processing and gets repeatedly retransmitted by the JMS destination. It creates an infinite retry loop that can drain system resources if not handled properly.
- MDB receives a message and begins processing (within a container-managed transaction)
- Processing fails — MDB throws a system exception OR calls
MessageDrivenContext.setRollbackOnly() - Transaction rolls back → message acknowledgment is NOT sent to JMS destination
- JMS destination detects no ack → retransmits the same message to the container
- Container assigns MDB instance → same failure occurs → another rollback
- Loop continues indefinitely until manually stopped or a Dead Letter Queue (DLQ) intervenes
- Message contains ticker symbol “INVALID”
- MDB cracks open message, doesn’t find a valid stock
- MDB throws system exception or calls
setRollbackOnly() - Transaction rollback → message not acknowledged
- JMS retransmits “INVALID” → MDB fails again → infinite loop
- Caused by transaction rollback in MDB (system exception or
setRollbackOnly()) - MDB is stateless — doesn’t remember that this message previously failed
- Infinite loop consumes CPU, memory, and JMS resources
- Solution: Dead Letter Queue (DLQ) after N retry attempts (configured in MOM)
- Also solved by catching exceptions and acknowledging the message (don’t rollback)
- Built from: MDB — poison messages occur in MDB message processing
- Built from: CMT — rollback behavior is controlled by container-managed transactions
- Related: Queue Partitioning — separate queues reduce poison message impact
- Related: setRollbackOnly() — method that triggers rollback causing poison messages
- Contrasts with: Session Bean — session beans don’t process queued messages, so no poison messages
- MDB has no memory of prior failures — will retry the same message indefinitely
- Poison messages can cascade — one bad message can block a queue if consumers keep retrying
setRollbackOnly()is the programmatic way to cause a poison message (besides throwing exceptions)- Some MOMs have configurable “max retries” before moving message to DLQ
- Poison messages also occur with BMP entity beans that rollback transactions repeatedly