Receiving, Visibility, and the DLQ
The consumer half of the pipeline: pull messages, hold them invisibly
while you work, ack the ones you finish, and let the retry budget move the
rest to the dead letter queue. The examples here use a short visibility timeout and --max-receives 2.
Receiving
snug --output json mq receive -q order-processing -m 10 -w 20 --visibility 60{
"messages": [
{
"message_id": "msg_UFRSuwjFZsLv",
"receipt_handle": "rh_PnsQcyJTLYSs",
"body": { "order_id": 123, "total": 49.99 },
"attributes": { "type": "created" },
"system_attributes": {
"sender_id": "docs-wave",
"sent_timestamp": "2026-08-28T05:01:36.587054Z",
"approximate_receive_count": 1,
"approximate_first_receive_timestamp": "2026-08-28T05:01:41.841845Z",
"sequence_number": "00000000000000000001",
"message_group_id": null
}
}
]
}-mcaps the batch at 1-10 messages (default 1).-wlong-polls: on an empty queue,-w 3blocked for 3.1 seconds before returning{"messages": []}(verified withtime);0returns immediately. Maximum 20 seconds.--visibilityoverrides the queue's default timeout for just this receive.
The visibility loop
Receiving does not remove a message - it hides it for the visibility timeout. Verified sequence on a queue with a 2-second timeout:
- Receive returns the message with
approximate_receive_count: 1. - A receive during the window returns
{"messages": []}. - After the window lapses un-acked, the same
message_idcomes back with a newreceipt_handleandapproximate_receive_count: 2.
Deleting with the receipt handle is the acknowledgment. While holding a message you can also move the deadline:
snug mq visibility -q order-processing -r rh_rtgPXjcuLAtw -t 120 # need more time
snug mq visibility -q order-processing -r rh_rtgPXjcuLAtw -t 0 # give it back nowAfter -t 120 the message stayed invisible to an
immediate receive; after -t 0 it was immediately receivable again (with
its receive count incremented). Acks batch too:
snug mq delete-batch -q order-processing -r rh_YufpsDCuRCeP,rh_wSJspNhwWEeBThe response lists successful and failed receipt handles; the batch is
capped at 10.
Receipt handle lifetimes
The rules differ by operation:
deleteis lenient. A receipt handle from an earlier receive of the same message still acks it, even after the visibility window lapsed and a newer handle was issued. Deleting late is safe - but if another consumer re-received the message in between, your late ack deletes their in-flight copy.visibilityis strict. Changing visibility with a lapsed handle fails with400 invalid_receipt_handle- you can only move the deadline of a delivery you still hold.- A handle the server never issued fails with
400 receipt_handle_expired.
The DLQ loop
A queue created with --dlq and --max-receives N retries each message N
times; the move to the DLQ happens during receive processing. Verified with
--max-receives 2: two un-acked receives, then the third receive returned
{"messages": []} and the message appeared in the DLQ.
snug --output json mq dlq get -q order-processing # SOURCE queue name, not the DLQ's{
"source_queue": "order-processing",
"dlq_name": "failed-orders",
"approximate_message_count": 1,
"oldest_message_age_seconds": 0,
"sample_messages": [
{
"message_id": "msg_xwdTdpyWajHw",
"original_queue": "order-processing",
"receive_count": 2,
"moved_to_dlq_at": "2026-08-28T05:02:33.636927Z",
"body": { "order_id": 666, "poison": true },
"reason": "max_receive_count_exceeded"
}
]
}dlq get on a queue with no DLQ configured fails with
400 no_dlq_configured - including the DLQ queue itself, so pointing the
command at failed-orders is the mistake to avoid.
After fixing the consumer, push everything back to the source queue:
snug --output json mq dlq redrive -q order-processing -m 100{ "messages_redriven": 1, "destination_queue": "order-processing" }A redriven message starts over: it comes back with a fresh message_id,
approximate_receive_count: 1, and a full retry budget (verified - the
poison message returned to the queue and was received normally). A message
that exceeds its budget on a queue with no DLQ configured is dropped
instead of moved.
Errors you will meet
404 queue_not_found- receive/delete against a queue that does not exist (or belongs to another user).404 topic_not_found- publish to a missing topic.400 receipt_handle_expired/400 invalid_receipt_handle- see receipt handle lifetimes.400 duplicate_message---dedup-idreused within the 5-minute window.400 no_dlq_configured- DLQ get/redrive on a queue without one.
The exact request and response schemas are in the Receiving and DLQ API references.