Skip to content

Some queue types RabbitMQ supports are not handled properly, causing ServiceControl usage collection to fail #5207

@boblangley

Description

@boblangley

Symptoms

The following error appears in the ServiceControl log:

Particular.LicensingComponent.BrokerThroughput.BrokerThroughputCollectorHostedService|Failed to gather throughput from broker|System.Text.Json.JsonException: Unknown queue type: rabbit_mqtt_qos0_queue

Who's affected

Anyone generating a usage report in ServiceControl with a RabbitMQ server that has the MQTT Plugin enabled and uses MQTT QoS 0 queues.

Root cause

Confirmed workarounds

  • None
Original bug report

Describe the bug

Description

ServiceControl usage tool throws an exception when scanning the queues with mqtt qos0 queue types. The error is thrown by the transport queue type converter.

Expected behavior

Usage tool skips those queue types and does not fail.

Actual behavior

An exception is thrown and the usage functionality does not complete.

Versions

Please list the version of the relevant packages or applications in which the bug exists.

  • ServiceControl 6.7.1, most likely in all versions that support collecting usage data.

Steps to reproduce

  1. Create a queue in the RabbitMQ broker that uses an unsupported queue type.
  2. Run the usage tool in ServiceControl
  3. Check log files for the exception above.

Relevant log output

2025-08-06 13:41:25.5385|00:01:09.798|56|Error|Particular.LicensingComponent.BrokerThroughput.BrokerThroughputCollectorHostedService|Failed to gather throughput from broker|System.Text.Json.JsonException: Unknown queue type: rabbit_mqtt_qos0_queue
   at NServiceBus.Transport.RabbitMQ.ManagementApi.QueueTypeConverter.Read(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options) in /_/src/NServiceBus.Transport.RabbitMQ/Administration/ManagementApi/Converters/QueueTypeConverter.cs:line 20
   at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
   at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
   at System.Text.Json.Serialization.JsonCollectionConverter`2.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, TCollection& value)
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
   at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
   at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
   at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsync(Stream utf8Json, CancellationToken cancellationToken)
   at System.Net.Http.Json.HttpClientJsonExtensions.<FromJsonAsyncCore>g__Core|12_0[TValue,TJsonOptions](HttpClient client, Task`1 responseTask, Boolean usingResponseHeadersRead, CancellationTokenSource linkedCTS, Func`4 deserializeMethod, TJsonOptions jsonOptions, CancellationToken cancellationToken)
   at NServiceBus.Transport.RabbitMQ.ManagementApi.ManagementClient.Get[T](String url, CancellationToken cancellationToken)
   at NServiceBus.Transport.RabbitMQ.ManagementApi.ManagementClient.GetQueues(Int32 page, Int32 pageSize, CancellationToken cancellationToken) in /_/src/NServiceBus.Transport.RabbitMQ/Administration/ManagementApi/ManagementClient.cs:line 127
   at ServiceControl.Transports.RabbitMQ.RabbitMQQuery.<>c__DisplayClass5_0.<<GetQueueNames>b__0>d.MoveNext() in /_/src/ServiceControl.Transports.RabbitMQ/RabbitMQQuery.cs:line 78
--- End of stack trace from previous location ---
   at Polly.ResiliencePipeline.<>c__10`1.<<ExecuteAsync>b__10_0>d.MoveNext()
--- End of stack trace from previous location ---
   at Polly.Outcome`1.GetResultOrRethrow()
   at Polly.ResiliencePipeline.ExecuteAsync[TResult](Func`2 callback, CancellationToken cancellationToken)
   at ServiceControl.Transports.RabbitMQ.RabbitMQQuery.GetQueueNames(CancellationToken cancellationToken)+MoveNext() in /_/src/ServiceControl.Transports.RabbitMQ/RabbitMQQuery.cs:line 78
   at ServiceControl.Transports.RabbitMQ.RabbitMQQuery.GetQueueNames(CancellationToken cancellationToken)+System.Threading.Tasks.Sources.IValueTaskSource<System.Boolean>.GetResult()
   at Particular.LicensingComponent.BrokerThroughput.BrokerThroughputCollectorHostedService.GatherThroughput(CancellationToken stoppingToken) in /_/src/Particular.LicensingComponent/BrokerThroughput/BrokerThroughputCollectorHostedService.cs:line 69
   at Particular.LicensingComponent.BrokerThroughput.BrokerThroughputCollectorHostedService.GatherThroughput(CancellationToken stoppingToken) in /_/src/Particular.LicensingComponent/BrokerThroughput/BrokerThroughputCollectorHostedService.cs:line 69
   at Particular.LicensingComponent.BrokerThroughput.BrokerThroughputCollectorHostedService.ExecuteAsync(CancellationToken stoppingToken) in /_/src/Particular.LicensingComponent/BrokerThroughput/BrokerThroughputCollectorHostedService.cs:line 48

Additional Information

Workarounds

Possible solutions

Update the queue type parser to handle all RabbitMQ queue types gracefully.

Additional information

The QueueTypeConverter System.Text.Json converter throws an exception if a queue type that is not hard coded in the switch statement is indicated in the JSON.

https://github.com/Particular/NServiceBus.RabbitMQ/blob/master/src/NServiceBus.Transport.RabbitMQ/Administration/ManagementApi/Converters/QueueTypeConverter.cs#L15-L20

This converter is triggered during the ManagementClient.GetQueues method call when deserializing the response:

https://github.com/Particular/NServiceBus.RabbitMQ/blob/master/src/NServiceBus.Transport.RabbitMQ/Administration/ManagementApi/ManagementClient.cs#L121

The ServiceControl.Transport.RabbitMQ library calls the ManagementClient.GetQueues method:

https://github.com/Particular/ServiceControl/blob/master/src/ServiceControl.Transports.RabbitMQ/RabbitMQQuery.cs#L78

[!NOTE]
The ManagementClient class is accessed via internals visible to by the ServiceControl.Transport.RabbitMQ library.

Backported to

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions