I think I have figured it out.
After gathering RabbitMQ logs I got this clue:
2026-04-07 16:59:02.368622+00:00 [info] <0.821.0> Connection from AMQP 1.0
container 'ID:7629abd0-e49d-4c5a-826f-7dbc19e0a9d4:2:1': user 'guest'
authenticated using SASL mechanism ANONYMOUS and granted access to vhost '/'
2026-04-07 16:59:02.390733+00:00 [warning] <0.827.0> Received Flow frame
for unknown link handle: {'v1_0.flow',
2026-04-07 16:59:02.390733+00:00 [warning] <0.827.0> {uint,1},
2026-04-07 16:59:02.390733+00:00 [warning] <0.827.0> {uint,1600},
2026-04-07 16:59:02.390733+00:00 [warning] <0.827.0> {uint,1},
2026-04-07 16:59:02.390733+00:00 [warning] <0.827.0> {uint,2147483647},
2026-04-07 16:59:02.390733+00:00 [warning] <0.827.0> {uint,1},
2026-04-07 16:59:02.390733+00:00 [warning] <0.827.0> {uint,0},
2026-04-07 16:59:02.390733+00:00 [warning] <0.827.0> {uint,0},
2026-04-07 16:59:02.390733+00:00 [warning] <0.827.0>
undefined,false,undefined,
2026-04-07 16:59:02.390733+00:00 [warning] <0.827.0> undefined}
2026-04-07 16:59:02.390983+00:00 [warning] <0.827.0> Closing session for
connection <0.821.0>: {'v1_0.error',
2026-04-07 16:59:02.390983+00:00 [warning] <0.827.0> {symbol,
2026-04-07 16:59:02.390983+00:00 [warning] <0.827.0>
<<"amqp:session:unattached-handle">>},
2026-04-07 16:59:02.390983+00:00 [warning] <0.827.0> {utf8,
2026-04-07 16:59:02.390983+00:00 [warning] <0.827.0> <<"Unattached link
handle: 1">>},
2026-04-07 16:59:02.390983+00:00 [warning] <0.827.0> undefined}
So, looks like we are trying to interact with a link which is not fully
ready on the broker side.
Therefore I went forward and implemented reaction to this API:
/**
* @return a {@link Future} that will be completed when the remote
opens this {@link Link}.
*/
Future<T> openFuture();
Something like this, for example:
senderToReturn =
this.connectionFactory
.getConnection()
.openAnonymousSender(this.senderOptions);
Future<Sender> openFuture = senderToReturn.openFuture();
this.sender = ProtonUtils.toSupplier(openFuture,
this.senderOptions.openTimeout()).get();
So far my tests are OK.
I think this ticket can be closed.
Thank you for your support!
On Tue, Mar 31, 2026 at 2:03 PM Artem Bilan <[email protected]>
wrote:
> I'm still investigating the cause of premature and sproradic session
> closure in my tests.
> However I want to point that RabbitMQ does support several links per
> session.
> My original impression was wrong.
> Either way need to figure out how to pull RabbitMQ logs from
> Testcontainers when that test fails.
>
> On Mon, Mar 30, 2026 at 2:36 PM Timothy Bish <[email protected]> wrote:
>
>> I don't really know anything about the RabbitMQ implementation so I
>> cannot really provide any insights into why it closed the session but I
>> would guess that is the cause of your failed test, it might be sporadic due
>> to this being something that happens after your test is done or in the
>> process of closing down so it only gets seems sometimes.
>>
>> This is where your session was closed by the remote so I'd suggest
>> looking into why you might get the specific error and session closure.
>> <- AMQP:[310426339:0]
>> End{error=Error{condition=amqp:session:unattached-handle,
>> description='Unattached link handle: 2', info=null}}
>> -> AMQP:[310426339:0] End{error=null}
>>
>> The protonj2 client allows you to manage sessions as you like so if
>> Rabbit needs a session per producer or consumer that is definitely
>> something you can do, if you are using the connection level APIs to create
>> them you would already be using a single session for the entirety of the
>> connection's resources.
>>
>> On Mon, Mar 30, 2026 at 1:05 PM Artem Bilan via users <
>> [email protected]> wrote:
>>
>>> Hi Tim!
>>> Thank you for the tip!
>>> I added that tracing for frames, however it does not say me too much.
>>> Just test sporadically fails like with this trace:
>>>
>>> -> SASL:[310426339:0] AMQP,3,1,0,0
>>> <- SASL:[310426339:0] AMQP,3,1,0,0
>>> <- SASL:[310426339:0] SaslMechanisms{saslServerMechanisms=[PLAIN,
>>> AMQPLAIN, ANONYMOUS]}
>>> 2026-03-28 05:35:21,659 WARN
>>> org.apache.qpid.protonj2.engine.sasl.client.SaslMechanismSelector
>>> [ClientConnection :(ID:a54be302-d329-4598-8bfc-55b1cfed2e33:1:1): I/O
>>> Thread] : Caught exception while trying to create SASL mechanism AMQPLAIN:
>>> No Matching SASL Mechanism with name: AMQPLAIN
>>> -> SASL:[310426339:0] SaslInit{mechanism=ANONYMOUS, initialResponse="",
>>> hostname='localhost'}
>>> <- SASL:[310426339:0] SaslOutcome{code=OK, additionalData=null}
>>> -> AMQP:[310426339:0] AMQP,0,1,0,0
>>> <- AMQP:[310426339:0] AMQP,0,1,0,0
>>> -> AMQP:[310426339:0] Open{
>>> containerId='ID:a54be302-d329-4598-8bfc-55b1cfed2e33:1:1',
>>> hostname='localhost', maxFrameSize=65536, channelMax=65535,
>>> idleTimeOut=30000, outgoingLocales=null, incomingLocales=null,
>>> offeredCapabilities=null, desiredCapabilities=[ANONYMOUS-RELAY],
>>> properties=null}
>>> -> AMQP:[310426339:0] Begin{remoteChannel=null, nextOutgoingId=0,
>>> incomingWindow=1600, outgoingWindow=2147483647, handleMax=null,
>>> offeredCapabilities=null, desiredCapabilities=null, properties=null}
>>> -> AMQP:[310426339:0]
>>> Attach{name='receiver-ID:a54be302-d329-4598-8bfc-55b1cfed2e33:1:1:1:1',
>>> handle=0, role=RECEIVER, sndSettleMode=UNSETTLED, rcvSettleMode=FIRST,
>>> source=Source{address='/queues/enable_amqp_queue1', durable=NONE,
>>> expiryPolicy=LINK_DETACH, timeout=0, dynamic=false,
>>> dynamicNodeProperties=null, distributionMode=null, filter=null,
>>> defaultOutcome=Modified{deliveryFailed=true, undeliverableHere=false,
>>> messageAnnotations=null}, outcomes=[amqp:accepted:list, amqp:rejected:list,
>>> amqp:released:list, amqp:modified:list], capabilities=null},
>>> target=Target{address='/queues/enable_amqp_queue1', durable=NONE,
>>> expiryPolicy=SESSION_END, timeout=0, dynamic=false,
>>> dynamicNodeProperties=null, capabilities=null}, unsettled=null,
>>> incompleteUnsettled=null, initialDeliveryCount=null, maxMessageSize=null,
>>> offeredCapabilities=null, desiredCapabilities=null, properties=null}
>>> -> AMQP:[310426339:0] Flow{nextIncomingId=null, incomingWindow=1600,
>>> nextOutgoingId=0, outgoingWindow=2147483647, handle=0, deliveryCount=null,
>>> linkCredit=55, available=null, drain=false, echo=null, properties=null}
>>> -> AMQP:[310426339:0]
>>> Attach{name='receiver-ID:a54be302-d329-4598-8bfc-55b1cfed2e33:1:1:1:2',
>>> handle=1, role=RECEIVER, sndSettleMode=UNSETTLED, rcvSettleMode=FIRST,
>>> source=Source{address='/queues/enable_amqp_queue1', durable=NONE,
>>> expiryPolicy=LINK_DETACH, timeout=0, dynamic=false,
>>> dynamicNodeProperties=null, distributionMode=null, filter=null,
>>> defaultOutcome=Modified{deliveryFailed=true, undeliverableHere=false,
>>> messageAnnotations=null}, outcomes=[amqp:accepted:list, amqp:rejected:list,
>>> amqp:released:list, amqp:modified:list], capabilities=null},
>>> target=Target{address='/queues/enable_amqp_queue1', durable=NONE,
>>> expiryPolicy=SESSION_END, timeout=0, dynamic=false,
>>> dynamicNodeProperties=null, capabilities=null}, unsettled=null,
>>> incompleteUnsettled=null, initialDeliveryCount=null, maxMessageSize=null,
>>> offeredCapabilities=null, desiredCapabilities=null, properties=null}
>>> -> AMQP:[310426339:0] Flow{nextIncomingId=null, incomingWindow=1600,
>>> nextOutgoingId=0, outgoingWindow=2147483647, handle=1, deliveryCount=null,
>>> linkCredit=55, available=null, drain=false, echo=null, properties=null}
>>> -> AMQP:[310426339:0]
>>> Attach{name='receiver-ID:a54be302-d329-4598-8bfc-55b1cfed2e33:1:1:1:3',
>>> handle=2, role=RECEIVER, sndSettleMode=UNSETTLED, rcvSettleMode=FIRST,
>>> source=Source{address='/queues/enable_amqp_queue1', durable=NONE,
>>> expiryPolicy=LINK_DETACH, timeout=0, dynamic=false,
>>> dynamicNodeProperties=null, distributionMode=null, filter=null,
>>> defaultOutcome=Modified{deliveryFailed=true, undeliverableHere=false,
>>> messageAnnotations=null}, outcomes=[amqp:accepted:list, amqp:rejected:list,
>>> amqp:released:list, amqp:modified:list], capabilities=null},
>>> target=Target{address='/queues/enable_amqp_queue1', durable=NONE,
>>> expiryPolicy=SESSION_END, timeout=0, dynamic=false,
>>> dynamicNodeProperties=null, capabilities=null}, unsettled=null,
>>> incompleteUnsettled=null, initialDeliveryCount=null, maxMessageSize=null,
>>> offeredCapabilities=null, desiredCapabilities=null, properties=null}
>>> -> AMQP:[310426339:0] Flow{nextIncomingId=null, incomingWindow=1600,
>>> nextOutgoingId=0, outgoingWindow=2147483647, handle=2, deliveryCount=null,
>>> linkCredit=55, available=null, drain=false, echo=null, properties=null}
>>> <- AMQP:[310426339:0] Open{ containerId='rabbit@69e87c0daa99',
>>> hostname='null', maxFrameSize=131072, channelMax=63, idleTimeOut=30000,
>>> outgoingLocales=null, incomingLocales=null,
>>> offeredCapabilities=[LINK_PAIR_V1_0, ANONYMOUS-RELAY],
>>> desiredCapabilities=null, properties={node=rabbit@69e87c0daa99,
>>> cluster_name=rabbit@69e87c0daa99, copyright=Copyright (c) 2007-2026
>>> Broadcom Inc and/or its subsidiaries, information=Licensed under the MPL
>>> 2.0. Website: https://rabbitmq.com, platform=Erlang/OTP 27.3.4.9,
>>> product=RabbitMQ, version=4.2.5}}
>>> <- AMQP:[310426339:0] Begin{remoteChannel=0, nextOutgoingId=0,
>>> incomingWindow=400, outgoingWindow=4294967295, handleMax=255,
>>> offeredCapabilities=null, desiredCapabilities=null, properties=null}
>>> -> AMQP:[310426339:0]
>>> Attach{name='sender-ID:a54be302-d329-4598-8bfc-55b1cfed2e33:1:1:1:1',
>>> handle=3, role=SENDER, sndSettleMode=UNSETTLED, rcvSettleMode=FIRST,
>>> source=Source{address='null', durable=NONE, expiryPolicy=LINK_DETACH,
>>> timeout=0, dynamic=false, dynamicNodeProperties=null,
>>> distributionMode=null, filter=null, defaultOutcome=null,
>>> outcomes=[amqp:accepted:list, amqp:rejected:list, amqp:released:list,
>>> amqp:modified:list], capabilities=null}, target=Target{address='null',
>>> durable=NONE, expiryPolicy=SESSION_END, timeout=0, dynamic=false,
>>> dynamicNodeProperties=null, capabilities=null}, unsettled=null,
>>> incompleteUnsettled=null, initialDeliveryCount=0, maxMessageSize=null,
>>> offeredCapabilities=null, desiredCapabilities=null, properties=null}
>>> <- AMQP:[310426339:0]
>>> Attach{name='receiver-ID:a54be302-d329-4598-8bfc-55b1cfed2e33:1:1:1:1',
>>> handle=0, role=SENDER, sndSettleMode=UNSETTLED, rcvSettleMode=FIRST,
>>> source=Source{address='/queues/enable_amqp_queue1', durable=NONE,
>>> expiryPolicy=LINK_DETACH, timeout=0, dynamic=false,
>>> dynamicNodeProperties=null, distributionMode=null, filter=null,
>>> defaultOutcome=Released{}, outcomes=[amqp:accepted:list,
>>> amqp:rejected:list, amqp:released:list, amqp:modified:list],
>>> capabilities=null}, target=null, unsettled=null, incompleteUnsettled=null,
>>> initialDeliveryCount=0, maxMessageSize=null, offeredCapabilities=null,
>>> desiredCapabilities=null, properties=null}
>>> <- AMQP:[310426339:0]
>>> Attach{name='receiver-ID:a54be302-d329-4598-8bfc-55b1cfed2e33:1:1:1:2',
>>> handle=1, role=SENDER, sndSettleMode=UNSETTLED, rcvSettleMode=FIRST,
>>> source=Source{address='/queues/enable_amqp_queue1', durable=NONE,
>>> expiryPolicy=LINK_DETACH, timeout=0, dynamic=false,
>>> dynamicNodeProperties=null, distributionMode=null, filter=null,
>>> defaultOutcome=Released{}, outcomes=[amqp:accepted:list,
>>> amqp:rejected:list, amqp:released:list, amqp:modified:list],
>>> capabilities=null}, target=null, unsettled=null, incompleteUnsettled=null,
>>> initialDeliveryCount=0, maxMessageSize=null, offeredCapabilities=null,
>>> desiredCapabilities=null, properties=null}
>>> <- AMQP:[310426339:0]
>>> Attach{name='receiver-ID:a54be302-d329-4598-8bfc-55b1cfed2e33:1:1:1:3',
>>> handle=2, role=SENDER, sndSettleMode=UNSETTLED, rcvSettleMode=FIRST,
>>> source=Source{address='/queues/enable_amqp_queue1', durable=NONE,
>>> expiryPolicy=LINK_DETACH, timeout=0, dynamic=false,
>>> dynamicNodeProperties=null, distributionMode=null, filter=null,
>>> defaultOutcome=Released{}, outcomes=[amqp:accepted:list,
>>> amqp:rejected:list, amqp:released:list, amqp:modified:list],
>>> capabilities=null}, target=null, unsettled=null, incompleteUnsettled=null,
>>> initialDeliveryCount=0, maxMessageSize=null, offeredCapabilities=null,
>>> desiredCapabilities=null, properties=null}
>>> <- AMQP:[310426339:0]
>>> Attach{name='sender-ID:a54be302-d329-4598-8bfc-55b1cfed2e33:1:1:1:1',
>>> handle=3, role=RECEIVER, sndSettleMode=UNSETTLED, rcvSettleMode=FIRST,
>>> source=Source{address='null', durable=NONE, expiryPolicy=LINK_DETACH,
>>> timeout=0, dynamic=false, dynamicNodeProperties=null,
>>> distributionMode=null, filter=null, defaultOutcome=null,
>>> outcomes=[amqp:accepted:list, amqp:rejected:list, amqp:released:list,
>>> amqp:modified:list], capabilities=null}, target=Target{address='null',
>>> durable=NONE, expiryPolicy=SESSION_END, timeout=0, dynamic=false,
>>> dynamicNodeProperties=null, capabilities=null}, unsettled=null,
>>> incompleteUnsettled=null, initialDeliveryCount=null,
>>> maxMessageSize=16777216, offeredCapabilities=null,
>>> desiredCapabilities=null, properties=null}
>>> <- AMQP:[310426339:0] Flow{nextIncomingId=0, incomingWindow=400,
>>> nextOutgoingId=0, outgoingWindow=4294967295, handle=3, deliveryCount=0,
>>> linkCredit=170, available=null, drain=null, echo=null, properties=null}
>>> -> AMQP:[310426339:0] Transfer{handle=3, deliveryId=0, deliveryTag={0},
>>> messageFormat=0, settled=false, more=false, rcvSettleMode=null, state=null,
>>> resume=null, aborted=null, batchable=null} -
>>> "\x00Sp\xc0\x04\x02AP\x00\x00Ss\xd0\x00\x00\x00B\x00\x00\x00\x0a@
>>> "...(truncated)
>>> <- AMQP:[310426339:0] Transfer{handle=0, deliveryId=0,
>>> deliveryTag=DeliveryTag: {[0, 0, 0, 0]}, messageFormat=0, settled=false,
>>> more=false, rcvSettleMode=null, state=null, resume=null, aborted=null,
>>> batchable=null} - "\x00Sp\xc0\x07\x05AP\x00@AC
>>> \x00Sr\xc12\x04\xa3\x0ax-exchange\xa1"...(truncated)
>>> <- AMQP:[310426339:0] Disposition{role=RECEIVER, first=0, last=0,
>>> settled=true, state=Accepted{}, batchable=false}
>>> -> AMQP:[310426339:0] Flow{nextIncomingId=1, incomingWindow=1600,
>>> nextOutgoingId=1, outgoingWindow=2147483647, handle=0, deliveryCount=1,
>>> linkCredit=0, available=null, drain=false, echo=null, properties=null}
>>> -> AMQP:[310426339:0] Detach{handle=0, closed=true, error=null}
>>> -> AMQP:[310426339:0] Flow{nextIncomingId=1, incomingWindow=1600,
>>> nextOutgoingId=1, outgoingWindow=2147483647, handle=1, deliveryCount=0,
>>> linkCredit=0, available=null, drain=false, echo=null, properties=null}
>>> <- AMQP:[310426339:0] Detach{handle=0, closed=true, error=null}
>>> -> AMQP:[310426339:0] Flow{nextIncomingId=1, incomingWindow=1600,
>>> nextOutgoingId=1, outgoingWindow=2147483647, handle=2, deliveryCount=0,
>>> linkCredit=0, available=null, drain=false, echo=null, properties=null}
>>> <- AMQP:[310426339:0] Transfer{handle=1, deliveryId=1,
>>> deliveryTag=DeliveryTag: {[0, 0, 0, 0]}, messageFormat=0, settled=false,
>>> more=false, rcvSettleMode=null, state=null, resume=null, aborted=null,
>>> batchable=null} - "\x00Sp\xc0\x07\x05AP\x00@BC
>>> \x00Sr\xc12\x04\xa3\x0ax-exchange\xa1"...(truncated)
>>> -> AMQP:[310426339:0] Detach{handle=2, closed=true, error=null}
>>> <- AMQP:[310426339:0] Detach{handle=2, closed=true, error=null}
>>> -> AMQP:[310426339:0] Detach{handle=1, closed=true, error=null}
>>> <- AMQP:[310426339:0]
>>> End{error=Error{condition=amqp:session:unattached-handle,
>>> description='Unattached link handle: 2', info=null}}
>>> -> AMQP:[310426339:0] End{error=null}
>>> I don't know if that is the case, but might be that RabbitMQ requires
>>> explicit session management for consumer and/or producers.
>>> At the same time I saw somewhere that some AMQP brokers requires one
>>> session per connection...
>>> I might consider to add a session management into my components if that
>>> is the case.
>>>
>>> Any thoughts?
>>>
>>> Thank you very much!
>>>
>>> On 2026/03/26 14:40:32 Timothy Bish wrote:
>>> > On 3/25/26 11:59, Artem Bilan via users wrote:
>>> > > Hi there!
>>> > >
>>> > > I am developing Spring Framework client based on the QPid ProtonJ 2
>>> > > Client:
>>> > >
>>> https://github.com/spring-projects/spring-amqp/tree/main/spring-amqp-client/src
>>> .
>>> > >
>>> > > One of my tests is sporadically failing like:
>>> >
>>> > Without an AMQP frame trace it is not possible to say why this is
>>> > failing, it is valid for the error to occur if the session is remotely
>>> > closed. The configuration guide in the docs explains how to enable
>>> frame
>>> > tracing, with that enabled you can see the frames that arrived around
>>> > the time this error occurs.
>>> >
>>> >
>>> https://github.com/apache/qpid-protonj2/blob/main/protonj2-client-docs/Configuration.md#logging
>>> >
>>> >
>>> > >
>>> > > Caused by: java.lang.IllegalStateException: Cannot create new
>>> Receiver
>>> > > from closed Session
>>> > >
>>> > >
>>> > > at
>>> > >
>>> org.apache.qpid.protonj2.engine.impl.ProtonSession.checkSessionClosed(ProtonSession.java:670)
>>>
>>> > >
>>> > >
>>> > > at
>>> > >
>>> org.apache.qpid.protonj2.engine.impl.ProtonSession.receiver(ProtonSession.java:396)
>>>
>>> > >
>>> > >
>>> > > at
>>> > >
>>> org.apache.qpid.protonj2.engine.impl.ProtonSession.receiver(ProtonSession.java:59)
>>>
>>> > >
>>> > >
>>> > > at
>>> > >
>>> org.apache.qpid.protonj2.client.impl.ClientReceiverBuilder.createReceiver(ClientReceiverBuilder.java:142)
>>>
>>> > >
>>> > >
>>> > > at
>>> > >
>>> org.apache.qpid.protonj2.client.impl.ClientReceiverBuilder.receiver(ClientReceiverBuilder.java:61)
>>>
>>> > >
>>> > >
>>> > > at
>>> > >
>>> org.apache.qpid.protonj2.client.impl.ClientSession.internalOpenReceiver(ClientSession.java:391)
>>>
>>> > >
>>> > >
>>> > > at
>>> > >
>>> org.apache.qpid.protonj2.client.impl.ClientConnection.lambda$openReceiver$3(ClientConnection.java:279)
>>>
>>> > >
>>> > >
>>> > > at
>>> > >
>>> io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:148)
>>>
>>> > >
>>> > >
>>> > > at
>>> > >
>>> io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:141)
>>>
>>> > >
>>> > >
>>> > > at
>>> > >
>>> io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:535)
>>>
>>> > >
>>> > >
>>> > > at
>>> io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:405)
>>> > >
>>> > > at
>>> > >
>>> io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:1195)
>>>
>>> > >
>>> > >
>>> > > at
>>> > >
>>> io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
>>> > >
>>> > >
>>> > > I have a single connection object per test suite lifecycle.
>>> > > The code I try to create a Receiver is like:
>>> > > ClientReceiver receiver =
>>> > >
>>> (ClientReceiver)connection.openReceiver(queue,receiverOptions)
>>> > > .addCredit(this.initialCredits);
>>> > > Which apparently leads to the shared internal session in that
>>> connection.
>>> > >
>>> > > The broker I test against is RabbitMQ 4.2.
>>> > >
>>> > > So, the question is: if I'm doing something wrong or missing
>>> anything.
>>> > > I also do have a reconnectEnabled on the connection.
>>> > >
>>> > > Please, let me know if you need anything else.
>>> > >
>>> > > Thank you!
>>> > >
>>> > > Regards,
>>> > > Artem Bilan
>>> >
>>> >
>>> > --
>>> > Tim Bish
>>> >
>>>
>>
>>
>> --
>> --
>> Tim Bish
>>
>>