New State of Dapr Report 2026.|Get The Report
Diagrid
Back to Learn
Agent Framework Integrations

Using AWS Strands with Diagrid Catalyst Durable Execution

AWS Strands keeps the conversation alive across a restart, but not the execution. Diagrid Catalyst wraps a Strands agent so it resumes from the exact tool call a crash interrupted, without changing the agent code.

ByHaziqa Sajid
July 7, 202610 min read

The container dies four minutes into a fifteen-position portfolio job. By then, the AWS Strands agent running inside it has pulled holdings from the brokerage, priced every ticker, and scored a week of news sentiment on the top five names. Risk decomposition was up next. It never runs.

Ten minutes later, the same container comes back up. The Strands session manager restores the session. Every message is there. Every tool result is there. The agent has full continuity of what was said. And then the event loop starts from the top.

That gap is the whole problem. Strands saved everything the agent said and everything its tools returned. What it didn't save is where in the loop it was. For a research assistant, the difference doesn't matter much. For a portfolio agent that just spent fifteen billable LLM calls and half a dozen paid market data lookups, it matters a lot.

This article is about what it takes to close that gap without walking away from Strands. Strands stay. The agent code stays. What changes is the runtime underneath.

What Strands actually persist

Before getting into what's missing, it's worth being precise about what Strands provides, because it provides quite a lot.

Strands has a SessionManager abstraction that hooks into the agent's lifecycle. When the agent adds a message, when a tool call completes, or when the agent finishes a turn, each of those events triggers a write to a pluggable storage backend. The SDK ships with two of them out of the box: FileSessionManager for local disk and S3SessionManager for anything running on AWS. There are third-party managers, too, and you can implement your own SessionRepository for a database of your choice.

What gets persisted is the session: the conversation history, the tool results the agent has seen, and the agent's state. All of it is captured immediately as it changes, which means a Strands agent that crashes and comes back with the same session_id will pick up the conversation thread without missing anything.

For a chat-shaped workload like customer support, a coding assistant, or a voice interface, this is close to what you want. The whole point of the session is that the conversation survives a restart, so the user doesn't have to explain themselves twice.

Vertical flow diagram of a Strands agent turn: a user turn enters the Strands Agent, which makes an LLM call, a tool call, LLM reasoning, and a response. Each step (message added, tool result, completed turn) fires a MessageAddedEvent that is written to a SessionManager backend such as S3, File, or a custom store.

Fig 1: What Strands persist. Each lifecycle event, like a new message, a tool result, or a completed turn, is written to the session store as it happens. The conversation history survives a restart. The event loop that produced it does not.

The mechanism is per-message and synchronous with respect to the lifecycle events. Every time the agent adds something to its message history, that write hits the backend. There is no batching, no waiting for the turn to complete, no risk of “the last few messages didn't make it.” That's the strong version of what Strands gives you.

The weaker version, and this is the piece worth reading twice, is that the session is stored content, not stored execution. When a new agent instance boots with the same session_id, Strands re-hydrates the messages into a fresh event loop. The loop itself starts from scratch. The LLM reads the message history and decides what to do next, exactly as if it had just been handed that history for the first time.

That's a real distinction, and it's the one everything else in this article follows from.

Why the gap matters

If you're just resuming a conversation, restarting the loop is fine. The agent looks at the history, sees that the last message was the user asking a question, and answers it. Nothing about “restarting the loop” is a problem in that case.

The problem shows up in the middle of a multi-step task. Say the portfolio agent has already made four tool calls in the current turn, for example, fetched holdings, pulled prices, gotten news for the top three positions, and is on tool call five when the process dies.

On restart, Strands loads the session. The message history contains the five tool calls it made and the four responses it received, plus the partial reasoning that led to the fifth. The event loop then hands that history back to the LLM and asks, essentially, what would you like to do next?

Three things can happen next, and none of them are recovery in the sense you'd want:

The LLM can decide that the fifth tool call still needs to happen and re-issue it. That's the least bad outcome, but it means the client is paying for whatever that tool costs a second time. If it's a market data call with per-request billing, or an LLM-backed sub-agent, that shows up on the invoice.

The LLM can decide that the state is far enough along that it should just skip to synthesis. Now the fifth tool call never happens, and the final answer is missing a piece of data that the agent originally decided it needed.

Or the LLM can look at the same history and reason about it differently this time, because LLMs are non-deterministic. It might replay all five calls with slightly different parameters. It might call a sixth tool it didn't originally use. It might get to a different conclusion entirely. The re-execution isn't a resume; it's a fresh reasoning pass over the same input.

Two-part diagram. The original run before the crash is a linear chain: fetch holdings, fetch prices, get news on AAPL, get news on MSFT, risk decomp, and then the process is evicted. On restart with SessionManager, the session is restored and the LLM reads history, then the LLM decides among three paths: re-run tool 5, skip to synthesis, or take a new path entirely.

Fig 2: What “restart” looks like with only session persistence. The messages come back, but the event loop starts over. The LLM re-reasons over the history and picks one of three paths, none of which is a true resume.

There are secondary issues too, worth flagging even if they're not the main story. The Strands multi-agent session managers (for Graph and Swarm patterns) track the current state of the execution but don't persist individual agent histories, so recovery gets thinner as the topology grows. Per-message persistence also grows linearly with the length of the conversation, as every message is a write, which is fine for short chats but adds up for long-running task agents.

The main story, though, is the one above. Persistence is what an agent has said and seen. Execution is what an agent has done. Strands has the first. It doesn't have the second.

What durable execution adds

Durable execution is the runtime guarantee that persistence alone doesn't give you. The distinction is worth naming carefully.

Persistence is storage. It's a database write, an S3 put, a file append. It captures a fact that this message existed, this tool was called with these arguments, and this result came back. When you read it, you get the fact back.

Durable execution is a promise about what happens on restart. A durable runtime models the workflow as a sequence of activities, checkpoints the input and output of every activity as it completes, and on restart replays the history rather than re-executing the completed steps. When the workflow gets to the last completed activity, it picks up from the next one. The activities that have already run are not re-run. Their persisted results are handed back to the workflow as if they had just returned.

Applied to an agent, this means each tool call becomes a checkpointed activity. The tool runs once, its input and output are persisted, and if the process dies before the next tool call, the workflow engine resumes from that exact point on restart. The LLM doesn't re-reason over the whole history to figure out where it was. The engine already knows where it was.

The durable agents piece covers the full mechanism, including how checkpointing works, why replay is deterministic, and how retries fit in. What matters for a Strands developer is a smaller point: this isn't Strands versus durable execution. Durable execution runs underneath Strands, and the agent code stays the same.

Diagrid Catalyst is a reliable and secure platform for running agentic workloads in production, with the Dapr runtime, state stores, and message brokers managed for you. It's the platform that provides that durable runtime layer under the Strands agent, using the open-source Dapr project's workflow engine to do the checkpointing. Which is what the next section actually shows.

What changes in the code

Almost nothing.

That's the interesting part, and it's worth being concrete about it. The Strands agent definition doesn't change. The tools don't change. The model configuration doesn't change. The system prompt doesn't change. The way you decorate a function with @tool is the same. What you add is one import and one wrapper.

Take the budget analysis agent from the Strands Agents samples repo. A single Strands agent with custom tools, memory, and Bedrock Guardrails, walking through a personal-finance budget check. Making that agent durable doesn't touch a line of the sample's code. It adds this:

from diagrid.agent.strands import DaprWorkflowAgentRunner

# Agent, tools, and prompt unchanged from the sample.
# See: github.com/strands-agents/samples
runner = DaprWorkflowAgentRunner(
    name="budget-analyzer",
    agent=agent,
    max_iterations=10,
)

The DaprWorkflowAgentRunner wraps the existing agent. Once wrapped, every tool invocation the Strands event loop makes goes through the Dapr workflow engine as a checkpointed activity.

You can see the shape of that in the runbook. Trigger the agent. The first tool runs, and its result gets persisted. The second tool starts, and the process exits. When you bring the process back up, you don't have to re-invoke anything. The workflow engine looks at its saved state, sees that the first tool has already been completed, and resumes at the second, the one that was in flight when the crash happened. The LLM does not re-reason about which tool to call next. The engine already has the plan.

Architecture diagram: a request enters the DaprWorkflowAgentRunner, which passes it to an unchanged Strands Agent. The agent issues tool calls through the Dapr Workflow Engine, which runs each tool as an activity and checkpoints the result for Tool 1, Tool 2, and Tool 3. The engine persists state to a customer-managed state store such as Postgres or DynamoDB, and the agent returns a final response.

Fig 3: The wrapped agent. The Strands agent is unchanged and still handles reasoning and tool selection. The DaprWorkflowAgentRunner routes each tool invocation through the Dapr workflow engine, which checkpoints inputs and outputs into a customer-managed state store. On a crash, the engine replays completed activities and resumes at the next uncompleted one.

The full working example, which includes the crash test, the recovery, and the Catalyst project setup, is in the Diagrid Strands quickstart.

What you actually get back

The framing so far has been narrow: crashes and recovery. That's the headline capability, but the wrap does more than that. It's worth naming a few of the other things you pick up on the same code change, because they're the things that tend to matter as the agent moves from a laptop to a production environment.

The first, and the one to lead with, is automatic failure recovery from the exact tool call that the crash interrupted. The agent doesn't re-derive its plan from message history. The workflow engine already knows what has been completed. You do not pay for the tool calls that have already run, and the LLM does not get a chance to reason itself onto a different path.

The second is deterministic replay. Because every activity's inputs and outputs are checkpointed, past executions can be replayed for debugging or audit without hitting the LLM or the tool APIs a second time. In an environment where you have to explain to a compliance team what a portfolio agent actually did last Tuesday, that's the difference between “we have logs” and “we can show you the exact state at every step.”

The third is that the storage and model layers become genuinely pluggable. Strands' defaults and its strongest integrations point at Bedrock for the model and S3 for the session, which is exactly what you want if the whole stack lives inside AWS, but which becomes a rewrite the moment part of it needs to move. With Diagrid Catalyst underneath, the state store is a component you configure, not a client library you import. The same agent code runs against Postgres in one environment and DynamoDB in another.

And the fourth, which matters if the team is standardizing on infrastructure rather than a single framework, is that the durable execution layer is agnostic to the agent framework above it. The same DaprWorkflowAgentRunner pattern works with LangGraph, OpenAI Agents, the Microsoft Agent Framework, and others. A team choosing to run their agents on Diagrid Catalyst doesn't have to also choose one agent framework to run forever. Strands is the choice for this agent. Another team's LangGraph agent runs on the same platform. The durability guarantees are the same in both places.

None of these are marketing points. They're direct consequences of the fact that the durability layer sits underneath the framework rather than replacing it.

Where to go next

Here's what you now have a working model of. Strands' session management is real, and it's good at what it's designed for: keeping the conversation alive across restarts. What it doesn't do is keep the execution alive. When an agent that has already made real tool calls comes back from a crash, it comes back to a fresh event loop, and the LLM is left to reason its way back to where it was. Sometimes that works. Often it costs money, or produces a different answer, or quietly skips a step nobody notices was missing.

Durable execution closes that gap. Every tool call becomes a checkpointed activity. Restart resumes from the last completed one. Pairing Strands with Diagrid Catalyst is what makes that concrete. Just a single import, wrapper, and the agent code stays yours.

The natural next question is coordination. Once a single Strands agent is durable, how do multiple agents. For example, a portfolio analyst talking to a risk assistant talking to an execution agent that coordinates reliably across a longer-running system where any one of them might crash without stalling the others? That's a different problem, and it's the one the multi-agent orchestration piece picks up.

If you want to go deeper on the concepts, the Dapr University lesson on Dapr Agents is the fastest path to a working mental model of how durable execution works underneath the framework you're using. It's free, self-paced, and it walks through the same primitives that make the Strands integration work.

Ready to Go to Production?

Add durable execution to your AI agents in minutes. Start free, no credit card required.