Generated banner for AWS Lambda Durable Functions
Generated logo for AWS Lambda Durable Functions

AWS Lambda Durable Functions

AWSOfficialPublished by AWS

  • Skill
  • Tool integration
  • Cloud and DevOps
  • Apache-2.0
  • Low risk

From the author

Builds resilient, long-running, multi-step applications with AWS Lambda durable functions with automatic state persistence, retry logic, and orchestration for long-running executions. Covers the critical replay model, step operations, wait/callback patterns, error handling with saga pattern, testing with LocalDurableTestRunner. Triggers on phrases like lambda durable functions, durable execution, workflow orchestration, state machines, retry/checkpoint patterns, long-running stateful Lambda functions, saga pattern, human-in-the-loop callbacks, reliable serverless applications, context.step, context.wait, context.invoke, context.runInChildContext, withDurableExecution, DurableContext, UnrecoverableInvocationError, durable-execution-sdk, qualified ARN invocation, and durable handler replay.
— the description by AWS

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

This skill provides guidance and patterns for implementing durable, multi-step applications on AWS Lambda. It focuses on the replay model, which allows functions to maintain state across long-running executions—up to one year—by automatically checkpointing progress. The skill covers essential architectural patterns such as the saga pattern for error handling, wait/callback mechanisms for external system integration, and testing strategies using the local test runner. It emphasizes strict adherence to deterministic coding practices required by the replay model to ensure reliable execution.

What it does

  • Enables state persistence for long-running Lambda functions.
  • Provides patterns for implementing the saga pattern and compensating transactions.
  • Supports wait/callback patterns for integrating with external systems.
  • Offers guidance on testing durable functions locally and in the cloud.
  • Defines rules for deterministic code to prevent state corruption during replays.
  • Outlines requirements for qualified ARN invocation and IAM permissions.

When to use it

  • When building workflows that require execution times longer than standard Lambda timeouts.
  • When orchestrating multi-step processes that need to maintain state across interruptions.
  • When implementing reliable, fault-tolerant serverless integrations.
  • When managing complex state machines or human-in-the-loop approval processes.
  • When you need automatic retry logic and checkpointing for critical business processes.

Advantages

  • Resilient execution that survives infrastructure interruptions.
  • Simplified state management without external databases for simple workflows.
  • Native support for complex orchestration patterns like sagas and parallel execution.
  • Built-in replay-aware logging and error handling.
  • Local testing capabilities to validate complex workflow logic.

Usage examples

“How do I implement a multi-step workflow with a wait period in AWS Lambda?”
The agent provides a code example using context.step for logic and context.wait for the delay, ensuring the execution state is persisted correctly.
“Why is my durable function failing during replay?”
The agent helps diagnose non-deterministic code (like Date.now() or Math.random()) placed outside of steps, which causes state corruption.
“How do I handle errors in a long-running durable function?”
The agent explains the saga pattern and how to implement compensating transactions within steps to roll back state if a step fails.
“What are the requirements for invoking a durable Lambda function?”
The agent clarifies that durable functions must be invoked using a qualified ARN (version, alias, or $LATEST) and provides the corresponding AWS CLI command.

Install

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/aws/aws-lambda-durable-functions/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip aws-lambda-durable-functions.zip -d .agents/skills

Documentation by AWS

AWS Lambda durable functions

Build resilient multi-step applications and AI workflows that can execute for up to 1 year while maintaining reliable progress despite interruptions.

Works best with the AWS MCP server but is not required. All AWS interactions in this skill use standard AWS CLI commands that work in any environment with configured AWS credentials.

Critical Rules

Read these before writing any code. Each one is a constraint that will silently break a function if violated.

  1. Durable execution must be enabled at function creation time — it cannot be retrofitted. A new Lambda function must be created with durable execution turned on. Migrate the logic into the new function; do not attempt to install the SDK and wrap the handler of the existing function and expect it to work.

  2. Durable functions must be invoked with a qualified ARN — a specific version, an alias, or the literal $LATEST suffix. An unqualified function name will fail. See the Invocation Requirements section below for examples.

  3. Durable operations cannot be nested. You cannot call context.step(), context.wait(), or context.invoke() from inside another step's callback. Use context.runInChildContext() to group operations instead.

  4. All non-deterministic code must run inside steps. Date.now(), Math.random(), UUID generation, API calls, and database queries outside a step will produce different values on replay and corrupt execution state.

  5. Closure mutations are lost on replay - return values from steps

  6. Side effects outside steps repeat - use context.logger (replay-aware)

When to Load Reference Files

Load the appropriate reference file based on what the user is working on:

  • Getting started, basic setup, example, ESLint, or Jest setup -> see getting-started.md

  • Understanding replay model, determinism, or non-deterministic errors -> see replay-model-rules.md

  • Creating steps, atomic operations, or retry logic -> see step-operations.md

  • Waiting, delays, callbacks, external systems, or polling -> see wait-operations.md

  • Parallel execution, map operations, batch processing, or concurrency -> see concurrent-operations.md

  • Error handling, retry strategies, saga pattern, or compensating transactions -> see error-handling.md

  • Advanced error handling, timeout handling, circuit breakers, or conditional retries -> see advanced-error-handling.md

  • Testing, local testing, cloud testing, test runner, or flaky tests -> see testing-patterns.md

  • Deployment, CloudFormation, CDK, SAM, log groups, deploy, or infrastructure -> see deployment-iac.md

  • Advanced patterns, GenAI agents, completion policies, step semantics, or custom serialization -> see advanced-patterns.md

  • troubleshooting, stuck execution, failed execution, debug execution ID, execution history, execution error, why did my execution fail, execution timed out, callback not received, diagnose execution, or root cause execution -> see troubleshooting-executions.md

Quick Reference

Basic Handler Pattern

TypeScript:

import { withDurableExecution, DurableContext } from '@aws/durable-execution-sdk-js';

export const handler = withDurableExecution(async (event, context: DurableContext) => {
  const result = await context.step('process', async () => processData(event));
  return result;
});

Python:

from aws_durable_execution_sdk_python import durable_execution, DurableContext

@durable_execution
def handler(event: dict, context: DurableContext) -> dict:
    result = context.step(lambda _: process_data(event), name='process')
    return result
Python API Differences

The Python SDK differs from TypeScript in several key areas:

  • Steps: Use @durable_step decorator + context.step(my_step(args)), or inline context.step(lambda _: ..., name='...'). Prefer the decorator for automatic step naming.

  • Wait: context.wait(duration=Duration.from_seconds(n), name='...')

  • Exceptions: ExecutionError (permanent), InvocationError (transient), CallbackError (callback failures)

  • Testing: Use DurableFunctionTestRunner class directly - instantiate with handler, use context manager, call run(input=...)

Invocation Requirements

Durable functions require qualified ARNs (version, alias, or $LATEST):

# Valid
aws lambda invoke --function-name my-function:1 output.json
aws lambda invoke --function-name my-function:live output.json

# Invalid - will fail
aws lambda invoke --function-name my-function output.json

IAM Permissions

Your Lambda execution role MUST have the AWSLambdaBasicDurableExecutionRolePolicy managed policy attached. This includes:

  • lambda:CheckpointDurableExecution - Persist execution state

  • lambda:GetDurableExecutionState - Retrieve execution state

  • CloudWatch Logs permissions

Additional permissions needed for:

  • Durable invokes: lambda:InvokeFunction on target function ARNs

  • External callbacks: Systems need lambda:SendDurableExecutionCallbackSuccess and lambda:SendDurableExecutionCallbackFailure

Validation Guidelines

When writing or reviewing durable function code, ALWAYS check for these replay model violations:

  1. Non-deterministic code outside steps: Date.now(), Math.random(), UUID generation, API calls, database queries must all be inside steps

  2. Nested durable operations in step functions: Cannot call context.step(), context.wait(), or context.invoke() inside a step function — use context.runInChildContext() instead

  3. Closure mutations that won't persist: Variables mutated inside steps are NOT preserved across replays — return values from steps instead

  4. Side effects outside steps that repeat on replay: Use context.logger for logging (it is replay-aware and deduplicates automatically)

When implementing or modifying tests for durable functions, ALWAYS verify:

  1. All operations have descriptive names

  2. Tests get operations by NAME, never by index

  3. Replay behavior is tested with multiple invocations

  4. Use LocalDurableTestRunner for local testing

Security Considerations

  • Checkpoint data encryption: Execution state is persisted automatically. Enable KMS encryption on associated CloudWatch Log Groups to protect checkpointed data at rest.

  • Sensitive data in step results: Step return values are checkpointed and persisted. Do not return secrets, raw credentials, or PII from steps — store sensitive data in Secrets Manager or SSM Parameter Store and return references instead.

  • Input validation: Validate and sanitize event payloads at the handler entry point before passing data to steps.

  • Credential management: Retrieve secrets from AWS Secrets Manager or SSM Parameter Store within steps.

  • Callback payload validation: Data received via waitForCallback originates from external systems — validate and sanitize before processing.

  • Logging: Avoid DEBUG log level in non-development environments as it may expose step results and execution state. Enable CloudWatch Logs encryption with KMS.

Resources

Files

references/getting-started.md

Getting Started with AWS Lambda durable functions

Quick start guide for building your first durable function.

Onboarding

Step 1: Validate Prerequisites

Before using AWS Lambda durable functions, verify:

  1. AWS CLI is installed (2.33.22 or higher) and configured:

    aws --version
    aws sts get-caller-identity
  2. Runtime environment is ready:

    • For TypeScript/JavaScript: Node.js 22+ (node --version)

    • For Python: Python 3.11+ (python --version. Note that only Lambda runtime environments 3.13+ come with the Durable Execution SDK pre-installed. 3.11 is the minimum supported Python version by the Durable Execution SDK itself — use OCI to bring your own container image with an older Python runtime + Durable Execution SDK.)

  3. Deployment capability exists (one of):

    • AWS SAM CLI (sam --version) 1.153.1 or higher

    • AWS CDK (cdk --version) v2.237.1 or higher

    • Direct Lambda deployment access

Step 2: Select language and IaC framework
Language Selection

Default: TypeScript

Override syntax:

  • "use Python" → Generate Python code

  • "use JavaScript" → Generate JavaScript code

When not specified, ALWAYS use TypeScript

IaC framework selection

Default: CDK

Override syntax:

  • "use CloudFormation" → Generate YAML templates

  • "use SAM" → Generate YAML templates

When not specified, ALWAYS use CDK

Error Scenarios
Unsupported Language
  • List detected language

  • State: "Durable Execution SDK is not yet available for [framework]"

  • Suggest supported languages as alternatives

Unsupported IaC Framework
  • List detected framework

  • State: "[framework] might not support Lambda durable functions yet"

  • Suggest supported frameworks as alternatives

Step 3: Install SDK

For TypeScript/JavaScript:

npm install @aws/durable-execution-sdk-js
npm install --save-dev @aws/durable-execution-sdk-js-testing

For Python:

pip install aws-durable-execution-sdk-python
pip install aws-durable-execution-sdk-python-testing

Basic Handler

TypeScript:

import { withDurableExecution, DurableContext } from '@aws/durable-execution-sdk-js';

export const handler = withDurableExecution(async (event, context: DurableContext) => {
  // Execute a step with automatic retry
  const userData = await context.step('fetch-user', async () => 
    fetchUserFromDB(event.userId)
  );

  // Wait without compute charges
  await context.wait({ seconds: 5 });

  // Process in another step
  const result = await context.step('process', async () => 
    processUser(userData)
  );

  return { success: true, data: result };
});

Python:

from aws_durable_execution_sdk_python import durable_execution, DurableContext, durable_step, StepContext
from aws_durable_execution_sdk_python.config import Duration

@durable_step
def fetch_user(step_ctx: StepContext, user_id: str):
    return fetch_user_from_db(user_id)

@durable_step
def process_user_data(step_ctx: StepContext, user_data: dict):
    return process_user(user_data)

@durable_execution
def handler(event: dict, context: DurableContext) -> dict:
    user_data = context.step(fetch_user(event['userId']))
    context.wait(duration=Duration.from_seconds(5))
    result = context.step(process_user_data(user_data))
    return {'success': True, 'data': result}

Common Patterns

Multi-Step Workflow

TypeScript:

export const handler = withDurableExecution(async (event, context: DurableContext) => {
  const validated = await context.step('validate', async () => 
    validateInput(event)
  );
  
  const processed = await context.step('process', async () => 
    processData(validated)
  );
  
  await context.wait('cooldown', { seconds: 30 });
  
  await context.step('notify', async () => 
    sendNotification(processed)
  );
  
  return { success: true };
});
GenAI Agent (Agentic Loop)

TypeScript:

export const handler = withDurableExecution(async (event, context: DurableContext) => {
  const messages = [{ role: 'user', content: event.prompt }];

  while (true) {
    const { response, tool } = await context.step('invoke-model', async () =>
      invokeAIModel(messages)
    );

    if (tool == null) return response;

    const toolResult = await context.step(`tool-${tool.name}`, async () =>
      executeTool(tool, response)
    );
    
    messages.push({ role: 'assistant', content: toolResult });
  }
});

Python:

# Note: invoke_ai_model and execute_tool are decorated with @durable_step
@durable_execution
def handler(event: dict, context: DurableContext) -> str:
    messages = [{"role": "user", "content": event["prompt"]}]

    while True:
        result = context.step(invoke_ai_model(messages))

        if result.get("tool") is None:
            return result["response"]

        tool = result["tool"]
        tool_result = context.step(execute_tool(tool, result["response"]))
        messages.append({"role": "assistant", "content": tool_result})
Human-in-the-Loop Approval

TypeScript:

export const handler = withDurableExecution(async (event, context: DurableContext) => {
  const plan = await context.step('generate-plan', async () =>
    generatePlan(event)
  );

  const answer = await context.waitForCallback(
    'wait-for-approval',
    async (callbackId) => sendApprovalEmail(event.approverEmail, plan, callbackId),
    { timeout: { hours: 24 } }
  );

  if (answer === 'APPROVED') {
    await context.step('execute', async () => performAction(plan));
    return { status: 'completed' };
  }
  
  return { status: 'rejected' };
});

Python:

from aws_durable_execution_sdk_python.config import WaitForCallbackConfig

@durable_execution
def handler(event: dict, context: DurableContext) -> dict:
    # Note: generate_plan and perform_action are decorated with @durable_step
    plan = context.step(generate_plan(event))

    # Wait for external approval
    def submit_approval(callback_id: str, ctx):
        send_approval_email(event['approver_email'], plan, callback_id)

    answer = context.wait_for_callback(
        submitter=submit_approval,
        name='wait-for-approval',
        config=WaitForCallbackConfig(timeout=Duration.from_hours(24))
    )

    if answer == 'APPROVED':
        context.step(perform_action(plan))
        return {'status': 'completed'}
    
    return {'status': 'rejected'}
Saga Pattern (Compensating Transactions)

TypeScript:

export const handler = withDurableExecution(async (event, context: DurableContext) => {
  const compensations: Array<{ name: string; fn: () => Promise<void> }> = [];

  try {
    await context.step('book-flight', async () => flightClient.book(event));
    compensations.push({
      name: 'cancel-flight',
      fn: () => flightClient.cancel(event)
    });

    await context.step('book-hotel', async () => hotelClient.book(event));
    compensations.push({
      name: 'cancel-hotel',
      fn: () => hotelClient.cancel(event)
    });

    return { success: true };
  } catch (error) {
    for (const comp of compensations.reverse()) {
      await context.step(comp.name, async () => comp.fn());
    }
    throw error;
  }
});

Project Structure

TypeScript
my-durable-function/
├── src/
│   ├── handler.ts              # Main handler
│   ├── steps/                  # Step functions
│   │   ├── validate.ts
│   │   └── process.ts
│   └── utils/                  # Utilities
│       └── retry-strategies.ts
├── tests/
│   └── handler.test.ts         # Tests with LocalDurableTestRunner
├── infrastructure/
│   └── template.yaml           # SAM/CloudFormation
├── eslint.config.js            # ESLint configuration
├── jest.config.js              # Jest configuration
├── tsconfig.json               # TypeScript configuration
└── package.json
Python
my-durable-function/
├── src/
│   ├── handler.py              # Main handler
│   ├── steps/                  # Step functions
│   │   ├── __init__.py
│   │   ├── validate.py
│   │   └── process.py
│   └── utils/
│       └── retry_strategies.py
├── tests/
│   └── test_handler.py         # Tests with DurableFunctionTestRunner
├── infrastructure/
│   └── template.yaml           # SAM/CloudFormation
└── pyproject.toml              # Project configuration

ESLint Plugin Setup

Install the ESLint plugin to catch common durable function mistakes at development time:

npm install --save-dev @aws/durable-execution-sdk-js-eslint-plugin
Option A: Flat Config (eslint.config.js)
import durableExecutionPlugin from '@aws/durable-execution-sdk-js-eslint-plugin';

export default [
  {
    plugins: {
      '@aws/durable-execution-sdk-js': durableExecutionPlugin,
    },
    rules: {
      '@aws/durable-execution-sdk-js/no-nested-durable-operations': 'error',
    },
  },
];
import durableExecutionPlugin from '@aws/durable-execution-sdk-js-eslint-plugin';

export default [
  durableExecutionPlugin.configs.recommended,
  // Your other configs...
];
Option C: Legacy .eslintrc.json
{
  "plugins": ["@aws/durable-execution-sdk-js-eslint-plugin"],
  "extends": ["plugin:@aws/durable-execution-sdk-js-eslint-plugin/recommended"],
  "rules": {
    "@aws/durable-execution-sdk-js-eslint-plugin/no-nested-durable-operations": "error"
  }
}

What the plugin catches:

  • Nested durable operations inside step functions

  • Incorrect usage of durable context outside handler

  • Common replay model violations

Jest Configuration

jest.config.js:

module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  roots: ['<rootDir>/src'],
  testMatch: ['**/*.test.ts'],
  transform: {
    '^.+\\.ts$': 'ts-jest',
  },
  collectCoverageFrom: [
    'src/**/*.ts',
    '!src/**/*.d.ts',
  ],
};

Key Configuration:

  • preset: 'ts-jest' - Essential for TypeScript support

  • transform - Maps .ts files to ts-jest transformer

  • testMatch - Specifies test file patterns

Python Project Setup

Add aws-durable-execution-sdk-python-testing to your dev/test dependencies in pyproject.toml.

Development Workflow

TypeScript
  1. Write handler with durable operations

  2. Test locally with LocalDurableTestRunner

  3. Validate replay rules (no non-deterministic code outside steps)

  4. Deploy with qualified ARN (version or alias)

  5. Monitor execution state and logs

Python
  1. Write handler with @durable_execution decorator

  2. Test locally with DurableFunctionTestRunner and pytest

  3. Validate replay rules (no non-deterministic code outside steps)

  4. Deploy with qualified ARN (version or alias)

  5. Monitor execution state and logs

Key Concepts

  • Steps: Atomic operations with automatic retry and checkpointing

  • Waits: Suspend execution without compute charges (up to 1 year)

  • Child Contexts: Group multiple durable operations

  • Callbacks: Wait for external systems to respond

  • Map/Parallel: Process arrays or run operations concurrently

Setup Checklist

When starting a new durable function project:

TypeScript
  • [ ] Install dependencies (@aws/durable-execution-sdk-js, testing & eslint packages)

  • [ ] Create jest.config.js with ts-jest preset

  • [ ] Configure tsconfig.json with proper module resolution

  • [ ] Set up ESLint with durable execution plugin

  • [ ] Create handler with withDurableExecution wrapper

  • [ ] Write tests using LocalDurableTestRunner

  • [ ] Use skipTime: true for fast test execution

  • [ ] Verify TypeScript compilation: npx tsc --noEmit

  • [ ] Run tests to confirm setup: npm test

  • [ ] Review replay model rules (no non-deterministic code outside steps)

Python
  • [ ] Install aws-durable-execution-sdk-python

  • [ ] Install aws-durable-execution-sdk-python-testing and pytest for testing

  • [ ] Create handler with @durable_execution decorator

  • [ ] Define step functions with @durable_step decorator

  • [ ] Write tests using DurableFunctionTestRunner class

  • [ ] Run tests: pytest

  • [ ] Review replay model rules (no non-deterministic code outside steps)

Error Scenarios

Unsupported Language
  • List detected language

  • State: "Durable Execution SDK is not yet available for [language]"

  • List supported languages as alternatives

Next Steps

  • Review replay-model-rules.md to avoid common pitfalls

  • Explore step-operations.md for retry strategies

  • Learn wait-operations.md for external integrations

  • Check testing-patterns.md for comprehensive testing

Requirements and compatibility

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Needs: Needs an MCP server, Needs a command-line tool, Needs Python

Security

What our scanner noticed· Low risk

  • Instructions that use the network

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

Published by
AWS
Publisher
AWS (Official)
Source
aws/agent-toolkit-for-aws · skills/specialized-skills/serverless-skills/aws-lambda-durable-functions · commit 0a218f8
Licence
Apache License 2.0 (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • NOTICE (added by AutoGineer from NOTICE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced

AutoGineer is not affiliated with or endorsed by AWS.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download aws-lambda-durable-functions.zip