Skip to content

ACK Retry and the Dead Letter Queue

eric edited this page Jun 12, 2026 · 1 revision

ACK, Retry and the Dead Letter Queue

Delivery confirmation is negotiated per subscription and fully backwards compatible: subscriptions that don't ask for ACK keep the simple fire-and-forget / immediate-replay behavior.

Enabling ACK

// auto-ACK: the client ACKs after your handler (or its returned promise) resolves
consumer.subscribe(producer.name, 'task', async (data) => {
    await doWork(data);
}, { durable: true, ack: true });

// manual ACK: you decide when
consumer.subscribe(producer.name, 'task', (data, from, ack) => {
    doWork(data).then(() => ack());
}, { durable: true, manual_ack: true, ack_timeout: '30s' });

With ACK enabled the server includes a msgId in each delivery, waits for ACK { msgId }, and re-delivers on timeout (default 30 s, configurable per subscription via ack_timeout or server-wide via the ack_timeout option). If the handler throws / rejects in auto-ACK mode, no ACK is sent — the message retries.

Retry policy

consumer.subscribe(producer.name, 'task', handler, {
    durable: true,
    ack: true,
    retry: {
        max_attempts: 3,        // default 3
        delay: '5s',            // '500ms' | '5s' | '2m' | number (seconds)
        backoff: 'exponential'  // or 'fixed' (default)
    }
});

Each delivery carries delivery_attempt in the raw envelope so handlers can detect redelivery.

The dead-letter queue

After max_attempts un-ACKed deliveries, the message moves to the realm's DLQ with the reason recorded. Inspect and act on it:

  • CLInpm run manager, options 19–21
  • Web UI — Observability tab: list, Replay, Discard
  • Signed commandsdlq_list, dlq_replay, dlq_discard (Management Commands)
  • HTTP (read-only)GET /api/realms/{realm}/dlq (Observability HTTP API)

Replay re-enqueues the message for its original consumer, removes it from the DLQ, and delivers immediately when the consumer is online — the retry/ACK cycle starts fresh. Discard drops it permanently.

DLQ depth is visible as the tyo_mq_messages_dlq_total metric and via the stats command.

Delivery guarantees in practice

Subscription Guarantee
plain at-most-once (live sockets only)
durable (no ACK) delivered when online or on reconnect; removed on delivery
durable + ack at-least-once — make handlers idempotent

At-least-once means duplicates are possible (ACK lost in transit, consumer switching cluster nodes); design handlers to tolerate redelivery, keying on your own message identifiers where it matters.

Clone this wiki locally