Skip to content

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

Description

@nashjain

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

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 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.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions