Durable execution in Pydantic AI agents with AWS Lambda Durability Pydantic AI Harness now ships AWSLambdaDurability, a capability that checkpoints every model request, function tool call, MCP call, and dynamic-toolset resolution as an AWS Lambda durable functions step, the company said. The capability addresses stateless Lambda retries that otherwise re-run a handler from the first line, duplicating model requests, tool calls, tokens, and side effects such as a second refund. The AWS Durable Execution SDK requires Python 3.11 or newer, and an execution can span up to a year with waits suspending it without compute charges for on-demand functions. Picture this: an AI support agent spends fifteen minutes working on a ticket. It reads the order, calls the billing API, asks the model what to do next, calls another tool, and somewhere in the eleventh minute the Lambda invocation times out. Lambda retries it. The handler runs again from the first line, so the same model requests go out, the same tools run, the same tokens land on the same invoice, and the billing API issues a second refund for the ticket it already refunded. You now have spent double as many tokens, and have a duplicated refund. This might sound like a bug, but it is not. A Lambda handler is stateless, and a retry has nothing to resume from because nothing recorded what the run had already finished. AWS Lambda durable functions https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html let you build reliable, long-running workflows on Lambda. A durable function can pause, wait for external events, retry failed operations, and resume where it left off, even if Lambda recycles the execution environment. Under the hood, durable functions are regular Lambda functions that use a checkpoint and replay mechanism to track progress. An execution can span up to a year, and a wait suspends it without compute charges for on-demand functions. Pydantic AI Harness https://pydantic.dev/docs/ai/harness/ now ships AWSLambdaDurability https://pydantic.dev/docs/ai/harness/aws-lambda/ , a capability that makes the run durable: every model request, function tool call, MCP call, and dynamic-toolset resolution is checkpointed as an AWS Lambda durable functions https://docs.aws.amazon.com/lambda/latest/dg/durable-functions.html step. A resumed execution replays the handler from the top and serves completed steps from stored results. uv add logfire "pydantic-ai-harness aws-lambda " "pydantic-ai-slim bedrock " The AWS Durable Execution SDK needs Python 3.11 or newer. logfire carries the instrumentation behind every trace in this post. Put an agent in a handler and deploy it. This is the honest starting point, and for a lookup that finishes in four seconds it is also the finishing point: python from typing import Any import logfire from pydantic ai import Agent logfire.configure logfire.instrument pydantic ai agent = Agent 'bedrock:us.amazon.nova-pro-v1:0' @agent.tool plain def get weather city: str - str: return f'It is sunny in {city}.' def handler event: dict str, Any , context: Any - str: return agent.run sync str event 'prompt' .output The ceiling is the retry. When the invocation source retries a failed handler, such as an asynchronous or event-source invocation after a timeout, throttling, or a transient network error, the whole run starts again, so you pay for the completed work as well as the failed step. Attach the capability when you build the agent, then adapt the handler body with durable agent handler : python from typing import Any import logfire from aws durable execution sdk python import DurableContext, durable execution from pydantic ai import Agent from pydantic ai harness.aws lambda import AWSLambdaDurability, durable agent handler logfire.configure logfire.instrument pydantic ai agent = Agent 'bedrock:us.amazon.nova-pro-v1:0', name='support', capabilities= AWSLambdaDurability , @agent.tool plain def get weather city: str - str: return f'It is sunny in {city}.' @durable execution @durable agent handler async def handler event: dict str, Any , context: DurableContext - str: result = await agent.run str event 'prompt' return result.output Two decorators and one capability. @durable execution has to be outermost, because its wrapper is what Lambda invokes, and reversing the order raises a UserError when the handler is defined. Underneath, durable agent handler hosts the async agent on a background event loop and services its steps on the Lambda handler thread, which is how a synchronous durable API and an async agent run end up in one continuous step sequence. Deploy it with a durable configuration and invoke a published version, since in-flight executions are pinned to the version that started them: aws lambda create-function \ --function-name support-agent \ --runtime python3.13 \ --handler handler.handler \ --role