Dead Letter Policy
Field-level reference for MaxReceiveCount and MaxReceiveQueue, the QueueMessage properties that configure dead-letter routing in the C# SDK.
Which to use
This page is the field-level reference for MaxReceiveCount and MaxReceiveQueue. For the end-to-end task — sending a message, exhausting retries, and consuming the diverted message — see Dead Letter Queue.
Overview
MaxReceiveCount and MaxReceiveQueue are two properties on QueueMessage that together define the dead-letter policy for a single message. They are set once, at send time, and travel with the message — the retry ceiling is a producer decision, not something a consumer can override.
Gotchas: the receive count increments on every failed delivery — an explicit nack, an expired transaction, or a visibility timeout — not just deliberate rejections, so set the ceiling above your normal retry budget. The dead-letter channel is an ordinary queue with no special behavior: nothing drains it for you, so monitor it and build a reprocessing path or failures pile up silently.
Prerequisites
- KubeMQ server running on
localhost:50000 - C# SDK installed (
dotnet add package KubeMQ.SDK.CSharp)
Code
// KubeMQ .NET SDK — QueuesStream: Dead Letter Policy
//
// This example demonstrates configuring a dead letter queue (DLQ) with the downstream
// receiver API. After MaxReceiveCount failed attempts, the message moves to the DLQ
// channel. The receiver is used to poll and inspect DLQ messages.
//
// Prerequisites:
// - KubeMQ server running on localhost:50000
// - dotnet run
using KubeMQ.Sdk.Client;
using KubeMQ.Sdk.Queues;
using System.Text;
await using var client = new KubeMQClient(new KubeMQClientOptions
{
ClientId = "csharp-queues-dead-letter-policy-client",
});
await client.ConnectAsync();
Console.WriteLine("Connected to KubeMQ server");
// Send a message with DLQ configuration
await client.SendQueueMessageAsync(new QueueMessage
{
Channel = "csharp-queues.dlp-source",
Body = Encoding.UTF8.GetBytes("Order that will fail processing"),
MaxReceiveCount = 3,
MaxReceiveQueue = "csharp-queues.dlp-destination"
});
Console.WriteLine("Sent message with MaxReceiveCount=3 and DLQ configured");
Console.WriteLine("After 3 failed receive attempts, the message moves to 'csharp-queues.dlp-destination'");
await using var receiver = await client.CreateQueueDownstreamReceiverAsync();
// Check the DLQ via downstream receiver
var batch = await receiver.PollAsync(new QueuePollRequest
{
Channel = "csharp-queues.dlp-destination",
MaxMessages = 10,
WaitTimeoutSeconds = 5,
});
if (batch.HasMessages)
{
Console.WriteLine($"DLQ contains {batch.Messages.Count} message(s):");
foreach (var msg in batch.Messages)
{
Console.WriteLine($" {Encoding.UTF8.GetString(msg.Body.Span)}");
}
await batch.AckAllAsync();
}
else
{
Console.WriteLine("No messages in DLQ yet (need to exhaust retries first)");
}
Console.WriteLine("Done.");
Field reference
MaxReceiveCount(int) — the number of failed receives (unacknowledged, rejected, or expired-visibility) allowed before the broker reroutes the message.MaxReceiveCount = 3on aQueueMessagemeans the 3rd unacknowledged receive triggers the move.MaxReceiveQueue(string) — the channel name of the dead-letter destination. If empty, messages that exceedMaxReceiveCountare discarded rather than rerouted — always set it explicitly if you want failures preserved.- Both properties are set on the
QueueMessageat send time; there is no way to change the policy after the message has been queued. - Pair
MaxReceiveQueuewithExpirationSecondsif you also want messages to expire from the source queue before the receive limit is reached.
For the walkthrough of sending, exhausting retries, and consuming from the resulting DLQ, see Dead Letter Queue.
Related
- Dead Letter Queue — task-oriented walkthrough for DLQ routing
- C# SDK Reference
- Send & Receive
- Ack All
Was this page helpful?