When consuming messages in a transaction, the AMQP consumer accepts messages and specifies the transaction they are part of. AMQPProtocolConverter keeps a record of messages accepted in this way. When the transaction is committed, AMQPProtocolConverter assumes that the first and last messages in this 'dispatched in Tx' list form a range, and creates a ranged 'standard ack' MessageAck to cover the messages. The broker then checks that the acks it is processing match messages it has previously dispatched, in PrefetchSubscription#assertAckMatchesDispatched(MessageAck).
If the messages aren't added to the AMQP transaction in sequence, e.g. the assumed 'last id' is actually for a message dispatched before the assumed 'first id' in the sequence, the check fails even though all the messages being acked are in the dispatched list. There is also an implicit assumption in the processing that the ack range is an unbroken sequence, and as a result it would seem possible for messages to be acked that were not actually considered part of the AMQP transaction. This non-sequential ordering can for example happen because a client isn't releasing unconsumed prefetched messages after a rollback of consumed messages, or alternatively is processing higher priority messages before lower priority messages originally dispatched to it earlier.
To combat these issues, the AMQP transaction commit processing should be updated to use 'individual acks'.