RabbitMQ: default_queue_type / x-queue-type = "undefined" Bug and PRECONDITION_FAILED Errors
search cancel

RabbitMQ: default_queue_type / x-queue-type = "undefined" Bug and PRECONDITION_FAILED Errors

book

Article ID: 423308

calendar_today

Updated On:

Products

VMware Tanzu RabbitMQ VMware Tanzu Data Suite VMware Tanzu Data Suite VMware Tanzu Data Intelligence VMware Tanzu Data Services VMware Tanzu Data Services Solutions

Issue/Introduction

In some environments, queues in a given virtual host suddenly receive the argument x-queue-type = "undefined" even though their applications never set this argument. This results in channel exceptions such as:

PRECONDITION_FAILED - inequivalent arg 'x-queue-type' for queue '<queue>' in vhost '<vhost>': received the value 'undefined' of type 'longstr' but current is none

The RabbitMQ Management UI may show the queue type as classic, while rabbitmqctl list_queues  arguments reveals x-queue-type = "undefined" for some queues. This discrepancy leads to confusing behavior and queue declaration failures, especially after node restarts or definition imports or apps failures.

Environment

  • RabbitMQ 3.13.x (classic queues with new queue-type handling)
  • Erlang/OTP 26.x
  • Clusters where:
    • default_queue_type is unset or appears as undefined in vhost metadata or exported definitions, and/or
    • Queues are declared by applications that do not pass any x-arguments (e.g. plain .QueueDeclare(name, durable, exclusive, autoDelete) in .NET).
    • Management plugin enabled and sometimes using exported/imported JSON definitions.

Cause

The issue is triggered when a virtual host metadata default_queue_type field is set to an unsupported value such as `"undefined"` or `<<"undefined">>` (both can be considered string values). Such values have likely bypassed validation due to one of the other known DQT-related bugs:

 

Resolution

Standardize the default queue type to classic at both vhost and node level, and then normalize any existing queues that have x-queue-type = "undefined" or no type set.

Step 1: Set default queue type to classic. This ensures all future queue declarations without x-queue-type become classic queues.

    • From rabbitmqctl,
      • rabbitmqctl update_vhost_metadata <vhost_name> --default-queue-type classic
    • Or From rabbitmqadmin, 
      • rabbitmqadmin vhosts declare --name <vhost_name> --default-queue-type classic
        
    • Or Cluster‑wide default in rabbitmq.conf:
      • # supported values: quorum, stream, classic, or a custom type module
        default_queue_type = classic

         

This node‑wide DQT will be used by any vhost that does not override it.​

Step 2: Bulk‑fix vhost DQT with rabbitmqctl eval (advanced)

  • In environments with many vhosts and CLI access, this eval snippet sets default_queue_type = <<"classic">> for every vhost where it is unset or currently "undefined":
rabbitmqctl eval '
lists:foreach(
  fun(VHostName) ->
    VHost = rabbit_vhost:lookup(VHostName),
    Meta = vhost:get_metadata(VHost),
    case maps:get(default_queue_type, Meta, undefined) of
      undefined ->
        rabbit_db_vhost:merge_metadata(VHostName, #{default_queue_type => <<"classic">>}),
        io:format("Set DQT for virtual host ~p (was not set)~n", [VHostName]);
      <<"undefined">> ->
        rabbit_db_vhost:merge_metadata(VHostName, #{default_queue_type => <<"classic">>}),
        io:format("Set DQT for virtual host ~p (was <<\"undefined\">>)~n", [VHostName]);
      DQT ->
        io:format("Virtual host ~p already has DQT = ~p~n", [VHostName, DQT])
    end
  end,
  rabbit_vhost:list_names()),
ok.
'


This proactively prevents new queues in any vhost from inheriting a broken undefined default.

  • Optional: normalize existing queues’ x-queue-type to classic
    To keep the Management UI and CLI consistent and avoid x-queue-type = "undefined" in arguments, you can also patch existing queues:
rabbitmqctl eval '
lists:foreach(
  fun(Q) ->
    QName = amqqueue:get_name(Q),
    Args = amqqueue:get_arguments(Q),
    case rabbit_misc:table_lookup(Args, <<"x-queue-type">>) of
      undefined ->
        NewArgs = rabbit_misc:set_table_value(Args, <<"x-queue-type">>, longstr, <<"classic">>),
        rabbit_db_queue:update(QName, fun(Q0) -> amqqueue:set_arguments(Q0, NewArgs) end),
        io:format("Set x-queue-type for ~p to <<\"classic\">> (was not set)~n", [QName]);
      {longstr, <<"undefined">>} ->
        NewArgs = rabbit_misc:set_table_value(Args, <<"x-queue-type">>, longstr, <<"classic">>),
        rabbit_db_queue:update(QName, fun(Q0) -> amqqueue:set_arguments(Q0, NewArgs) end),
        io:format("Set x-queue-type for ~p to <<\"classic\">> (was <<\"undefined\">>)~n", [QName]);
      {_Type, Val} ->
        io:format("Queue ~p already has x-queue-type = ~p~n", [QName, Val])
    end
  end,
  rabbit_amqqueue:list()),
ok.
'


This makes all queues explicitly classic at the argument level so that Management UI no longer shows undefined, and future queue.declare calls that assume classic behavior will match the stored definition.