asyncapi / asyncapi/spec

Feature Request: model retry policies and dead-letter routing on operations

Open
#1,234 1 comment 0 reactions 1 assignee Claimed by @nashjain View on GitHub
💡 Proposal (RFC 1)
Dominant language
JavaScript
Stars
5.3k
Forks
382
Avg merge
7m
Merged PRs (30d)
4

Description

## Problem

AsyncAPI can describe the primary operation and an optional reply response, but it has no standard way to express the behaviour when message processing fails: retry policy, retry destination, or dead-letter destination. Implementations therefore rely on unvalidated vendor extensions, which limits interoperability.

## Proposal

Add first-class, operation-level support for retries and dead-letter routing. A `receive` operation should be able to declare:

- a `retry` route: channel and message references, maximum attempts, and a backoff policy;
- a `deadLetter` route: channel and message references for messages that cannot be recovered.

`retry` and `deadLetter` would be sibling properties of `reply`. This makes the possible outcomes of an operation visible at one level, while retaining existing channel and message reference conventions.

## Illustrative AsyncAPI Spec

```yaml
asyncapi: 3.0.0
info:
title: Order processing API
version: 1.0.0

channels:
orders:
address: orders
messages:
order:
$ref: "#/components/messages/Order"
orderRetries:
address: orders.retry
messages:
retryOrder:
$ref: "#/components/messages/RetryOrder"
orderDeadLetters:
address: orders.dlq
messages:
deadLetterOrder:
$ref: "#/components/messages/DeadLetterOrder"

operations:
processOrder:
action: receive
channel:
$ref: "#/channels/orders"
messages:
- $ref: "#/channels/orders/messages/order"
retry:
channel:
$ref: "#/channels/orderRetries"
messages:
- $ref: "#/channels/orderRetries/messages/retryOrder"
maxAttempts: 3
strategy:
type: exponential
initialDelaySeconds: 1
multiplier: 2
maxDelaySeconds: 60
deadLetter:
channel:
$ref: "#/channels/orderDeadLetters"
messages:
- $ref: "#/channels/orderDeadLetters/messages/deadLetterOrder"
waitTimeInSeconds: 15

components:
messages:
Order:
name: OrderMessage
title: Order Event
summary: Order message
contentType: application/json
headers:
type: object
properties:
CorrelationId:
type: string
description: Correlates the order across topics
examples:
- corr-bulk-90003
required:
- CorrelationId
payload::
type: object
RetryOrder:
name: RetryMessage
title: Retry Message Event
summary: Message sent to retry topic when processing fails
contentType: application/json
headers:
type: object
properties:
CorrelationId:
type: string
description: Correlates the order across topics
examples:
- corr-retry-90001
required:
- CorrelationId
payload:
type: object
properties:
originalMessage:
type: object
description: The original message payload that failed processing
additionalProperties: true
messageKey:
type: string
description: The message key for partitioning
examples:
- ORD-RETRY-90001
retryCount:
type: integer
description: Current retry attempt number
examples:
- 1
firstAttemptTimestamp:
type: string
format: date-time
description: Timestamp of the first processing attempt
examples:
- '2026-01-19T10:00:00Z'
lastAttemptTimestamp:
type: string
format: date-time
description: Timestamp of the last processing attempt
examples:
- '2026-01-19T10:00:05Z'
errorMessage:
type: string
description: Error message from the failed attempt
examples:
- 'Simulated transformation failure for order: ORD-RETRY-90001'
errorStackTrace:
type: string
description: Stack trace of the error
examples:
- 'io.specmatic.async.transformer.MessageTransformationException: ...'
required:
- originalMessage
- messageKey
- retryCount
- firstAttemptTimestamp
- lastAttemptTimestamp
DeadLetterOrder:
name: DLQ
title: Dead Letter Queue Event
summary: Message sent to the DLQ topic after retries are exhausted
contentType: application/json
headers:
type: object
properties:
CorrelationId:
type: string
description: Correlates the order across topics
examples:
- corr-dlq-90001
required:
- CorrelationId
payload:
type: object
properties:
originalMessage:
type: object
additionalProperties: true
messageKey:
type: string
totalRetries:
type: number
firstAttemptTimestamp:
type: string
format: date-time
failedTimestamp:
type: string
format: date-time
finalErrorMessage:
type: string
finalErrorStackTrace:
type: string
required:
- originalMessage
- messageKey
- totalRetries
- firstAttemptTimestamp
- failedTimestamp
- finalErrorMessage
```

This example expresses the observable recovery path only. A Kafka binding might implement it with retry and DLQ topics, while an SQS binding might use a redrive policy; neither implementation detail is required by the core object.

## Design considerations

- Reuse existing Operation Object conventions for channel and message references.
- Associate retries and dead-letter routing with the originating receive operation.
- Support fixed-delay, linear-backoff, and exponential-backoff retry strategies, with an optional maximum delay.
- Define the relationship between retry exhaustion and dead-letter routing.
- Keep broker-specific delivery semantics in protocol bindings.

## Prior art

[Specmatic](https://github.com/specmatic/labs/tree/main/kafka-sqs-retry-dlq) already implements this behavior through operation-level extensions named `x-specmatic-retry` and `x-specmatic-dlq`. That implementation provides a concrete starting point for the object shape and contract-test scenarios.

## Acceptance criteria

- The specification defines reusable schemas for retry policy and dead-letter routing for `receive` operation.
- Retry and DLQ does not apply to `send` or `reply` operations.
- An operation can reference retry and dead-letter channels and messages.
- Fixed, linear, and exponential delay strategies are representable.
- The specification states the relationship between retry exhaustion and dead-letter routing.
- Examples and schema validation cover the new fields.

## Scope

This proposal describes the observable asynchronous contract. It does not mandate how brokers implement retries, delay queues, redrive policies, or message headers.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.