# Dead Letter Queue (/sdks/cpp/how-to/queues/dead-letter-queue)



## Overview [#overview]

A &#x2A;*dead-letter queue (DLQ)** gives a poison message somewhere to go instead of looping through consumers forever. When a message keeps failing — a malformed payload, a downstream outage, a handler bug — retrying it forever wastes consumer cycles and blocks everything behind it. A DLQ takes that decision out of your hands: past a set number of failed attempts, the broker diverts the message to a separate channel instead of retrying it again.

Routing runs on two settings attached to the message: `SetMaxReceiveCount()` and `SetMaxReceiveQueue()`. Every failed delivery — a nack, a reject, or an expired visibility window — increments the receive count; past the threshold, the broker reroutes the message to the DLQ instead of redelivering it. The DLQ itself is an ordinary queue, consumed like any other channel.

**Gotchas:** the DLQ doesn't drain itself — a dedicated consumer must watch it. The count increments on *any* failed delivery, not just deliberate rejections — a slow consumer that lets the visibility window lapse counts the same as an explicit nack. A typo in the DLQ channel name quietly creates an unrelated channel instead of failing loudly.

## Prerequisites [#prerequisites]

* KubeMQ server running on `localhost:50000`
* C++ SDK installed (vcpkg or CMake FetchContent)
* C++17 compiler (GCC 9+, Clang 9+, MSVC 2019+)

## Code [#code]

```cpp title="main.cc"
// Example: queues/dead_letter_queue
//
// Demonstrates configuring a dead-letter queue (DLQ) for messages that
// exceed the maximum receive count. After the max attempts, messages
// are moved to the specified DLQ channel.
//
// Channel: cpp-queues.dead-letter-queue
// Client ID: cpp-queues-dead-letter-queue-client
//
// Run with a KubeMQ server on localhost:50000
// (see https://docs.kubemq.io/deploy).

#include <kubemq/kubemq.h>

#include <iostream>

int main() {
    std::cout << "[1] Connecting to localhost:50000" << std::endl;

    kubemq::ClientOptions options;
    options.set_address("localhost", 50000);
    options.set_client_id("cpp-queues-dead-letter-queue-client");

    auto client_result = kubemq::Client::Create(options);
    if (!client_result.ok()) {
        std::cerr << "[ERROR] Failed to create client: " << client_result.status().message()
                  << std::endl;
        return 1;
    }
    auto& client = *client_result;

    std::string channel = "cpp-queues.dead-letter-queue";
    std::string dlq_channel = channel + ".dlq";

    // Send a message with dead-letter queue configuration.
    // After 3 failed receive attempts, the message is moved to the DLQ.
    std::cout << "[2] Sending message with DLQ config (max_receives=3, dlq=" << dlq_channel << ")"
              << std::endl;
    auto msg_result = kubemq::QueueMessage::Builder()
                          .SetChannel(channel)
                          .SetBody("message with DLQ")
                          .SetMaxReceiveCount(3)
                          .SetMaxReceiveQueue(dlq_channel)
                          .Build();
    if (!msg_result.ok()) {
        std::cerr << "[ERROR] Build message: " << msg_result.status().message() << std::endl;
        return 1;
    }
    auto send_result = client->SendQueueMessage(*msg_result);
    if (!send_result.ok()) {
        std::cerr << "[ERROR] SendQueueMessage: " << send_result.status().message() << std::endl;
        return 1;
    }
    if (send_result->is_error) {
        std::cerr << "[ERROR] Send failed: " << send_result->error << std::endl;
        return 1;
    }
    std::cout << "[3] DLQ message sent: id=" << send_result->message_id
              << " (max_receives=3, dlq=" << dlq_channel << ")" << std::endl;

    // Close the client explicitly.
    // Note: The Client destructor also calls Close(), but explicit
    // cleanup is shown here for clarity and to match Go's defer pattern.
    auto close_status = client->Close();
    if (!close_status.ok()) {
        std::cerr << "[ERROR] Close failed: " << close_status.message() << std::endl;
        return 1;
    }
    std::cout << "[4] Client closed" << std::endl;

    return 0;
}
```

## How It Works [#how-it-works]

* Configures messages with `SetMaxReceiveCount()` and `SetMaxReceiveQueue()`.
* After exceeding the max receive count, messages are moved to the dead-letter queue.
* The dead-letter queue is a regular queue channel that collects failed messages.
* Useful for isolating and debugging messages that cannot be processed.

## Related [#related]

* [Pattern overview](/learn/queues/getting-started)
* [C++ SDK Reference](/sdks/cpp/reference/queues)
* [Ack & Reject](/sdks/cpp/how-to/queues/ack-reject)
* [Dead Letter Policy](/sdks/cpp/how-to/queues/dead-letter-policy)
