Quorum queues with combined names > 255 bytes crash in maybe_grow, member management CLI, and observer

Author: Vishal-770Created Sep 16, 2026Updated Sep 17, 2026

Context

While tracing the callers of rabbit_queue_type_util:qname_to_internal_name/1 as part of investigating cross-vhost naming collisions in #11038, I identified an existing, standalone bug where queues whose combined names exceed 255 bytes crash across member management operations and CLI diagnostics.


Description

When a quorum queue has a combined name (vhost + _ + name) exceeding 255 bytes, operations in rabbit_quorum_queue:maybe_grow/5, rabbit_queue_type_ra:modify_members_matching/6, and rabbit_observer_cli_quorum_queues:sheet_body/1 crash with {badmatch, {error, {too_long, ...}}}.


Background & Root Cause

Under the AMQP 0-9-1 specification, queue names can be up to 255 octets and virtual host names can also be up to 255 octets. When joined internally as <<VHost/binary, "_", Name/binary>>, a completely valid queue definition can easily exceed 255 bytes (e.g. a 10-byte vhost with a 250-byte queue name).

Because Erlang atoms cannot exceed 255 bytes, rabbit_queue_type_util:qname_to_internal_name/1 returns {error, {too_long, Name}} for these names.

During declaration in rabbit_quorum_queue:declare/2:

erlang
RaName = case qname_to_internal_name(QName) of
             {ok, A} ->
                 A;
             {error, {too_long, N}} ->
                 rabbit_data_coercion:to_atom(ra:new_uid(N))
         end,
LeaderId = {RaName, LeaderNode},
NewQ0 = amqqueue:set_pid(Q, LeaderId),

For names > 255 bytes, RabbitMQ generates a unique identifier via ra:new_uid/1 and stores {RaName, LeaderNode} in amqqueue:set_pid/2.

All standard queue operations (publishing, consuming, deleting, status queries, and recovery on node restart) correctly read RaName directly from amqqueue:get_pid(Q).

However, three specific call sites recompute the internal name from scratch using qname_to_internal_name/1 instead of reading amqqueue:get_pid(Q):

  1. rabbit_quorum_queue:maybe_grow/5 (line 1922):

    erlang
    {ok, RaName} = qname_to_internal_name(QName)

    When cluster size changes or policies require growing quorum queue replicas, this crashes with {badmatch, {error, {too_long, ...}}}. In contrast, line 1641 in do_add_member/4 already reads {RaName, _} = amqqueue:get_pid(Q0).

  2. rabbit_queue_type_ra:modify_members_matching/6 (line 167):

    erlang
    {ok, RaName} = rabbit_queue_type_util:qname_to_internal_name(QName)

    Administrative CLI commands (rabbitmq-queues add_member, delete_member, grow, shrink, and rebalance) iterate over queues in a vhost. If any matching queue in the vhost has a name > 255 bytes, the entire command crashes with {badmatch}.

  3. rabbit_observer_cli_quorum_queues:sheet_body/1 (line 141):

    erlang
    {ok, InternalName} = rabbit_queue_type_util:qname_to_internal_name(#resource{virtual_host = Vhost, name= Name})

    In line 130, rabbit_amqqueue:pid_of(Q) was already matched as {QName, _QNode} where QName is the Ra name. Calling qname_to_internal_name here is redundant and crashes rabbitmq-diagnostics observer when viewing quorum queues.


Edge Cases Analyzed

  • AMQP Protocol Compliance: Valid queue names allowed by AMQP 0-9-1 specifications trigger this naturally when combined with vhost names.
  • Normal Queues (<= 255 bytes): For all standard queues, amqqueue:get_pid(Q) contains the exact same RaName that qname_to_internal_name/1 would produce. Zero behavior change.
  • Process Liveness & Offline Nodes: amqqueue:get_pid(Q) is a local record field access on #amqqueue{}; it does not perform network RPCs or require the leader process to be responding.
  • Queue Types: This is isolated to Quorum Queues. While Streams also use Raft internally, their queue object stores a standard Erlang process pid() in amqqueue:get_pid(Q), not a {RaName, Node} tuple. Furthermore, Streams do not implement the rabbit_queue_type_ra behaviour, so they are safely ignored by modify_members_matching/6.
  • Zero Data Migration: This fix does not change how queue names are formatted or persisted on disk. Existing Ra state machines and member directories remain completely untouched.

Proposed Fix

Update the three call sites to read amqqueue:get_pid(Q):

  • In rabbit_quorum_queue:maybe_grow/5: replace qname_to_internal_name(QName) with {RaName, _} = amqqueue:get_pid(Q).
  • In rabbit_queue_type_ra:modify_members_matching/6: read RaName from amqqueue:get_pid(Q).
  • In rabbit_observer_cli_quorum_queues:sheet_body/1: use QName directly and remove the redundant qname_to_internal_name call.

Source: rabbitmq/rabbitmq-server