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



<Callout type="info" title="Which to use">
  This is the task-oriented guide for routing failed messages to a dead-letter queue via the single-send API. For the stream-based `SetMaxReceiveCount`/`SetMaxReceiveQueue` policy option and its edge cases, see [Dead Letter Policy](./dead-letter-policy).
</Callout>

## 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`
* Go SDK installed (`go get github.com/kubemq-io/kubemq-go/v2`)

## Code [#code]

```go title="main.go"
// 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: go-queues.dead-letter-queue
// Client ID: go-queues-dead-letter-queue-client
//
// Run with a KubeMQ server on localhost:50000
// (see https://docs.kubemq.io/deploy).
package main

import (
	"context"
	"fmt"
	"log"
	"time"

	"github.com/kubemq-io/kubemq-go/v2"
)

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	client, err := kubemq.NewClient(ctx,
		kubemq.WithAddress("localhost", 50000),
		kubemq.WithClientId("go-queues-dead-letter-queue-client"),
	)
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()

	channel := "go-queues.dead-letter-queue"
	dlqChannel := channel + ".dlq"

	// Send a message with dead-letter queue configuration.
	// After 3 failed receive attempts, the message is moved to the DLQ.
	dlqMsg := kubemq.NewQueueMessage().
		SetChannel(channel).
		SetBody([]byte("message with DLQ")).
		SetMaxReceiveCount(3).
		SetMaxReceiveQueue(dlqChannel)

	result, err := client.SendQueueMessage(ctx, dlqMsg)
	if err != nil {
		log.Fatal(err)
	}
	if result.IsError {
		log.Fatalf("Send failed: %s", result.Error)
	}
	fmt.Printf("DLQ message sent: id=%s (max receives=3, dlq=%s)\n",
		result.MessageID, dlqChannel)
}

```

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

1. `kubemq.NewQueueMessage().SetMaxReceiveCount(3).SetMaxReceiveQueue(dlqChannel)` configures a dead-letter policy inline on the message at send time; no separate channel-level configuration is required.
2. When a consumer polls and nacks the message (or lets the transaction expire) more than 3 times, the broker automatically moves it to `dlqChannel` instead of redelivering it.
3. The DLQ channel name is `go-queues.dead-letter-queue.dlq` — it is a regular queue that can be polled for investigation or reprocessing.
4. `result.IsError` on the send response confirms the message was accepted with its DLQ policy; the policy is stored with the message in the broker.

## Related [#related]

* [Pattern overview](/learn/queues/getting-started)
* [Go SDK Reference](/sdks/go/reference)
* [Send & Receive](/sdks/go/tutorials/send-receive)
* [Ack All](/sdks/go/how-to/queues/ack-all)
