Published 2026-09-23 · Reviewed 2026-09-23

Before your automation retries, make the action idempotent

Retries turn a brief failure into a duplicate side effect unless the action can be repeated safely. A practical checklist for stable operation identifiers, precondition checks, outcome records and bounded retries, and an honest account of what it cannot promise.

  • automation
  • reliability
  • local ai agents
  • home automation

The retry that quietly runs twice

A routine fires at 07:00, asks a device to turn on a hallway light, and gets no reply before the connection times out. The retry logic does the sensible thing and tries again. The light comes on — twice as bright, because two "on" commands arrived. Nothing crashed. The log even looks healthy. The duplicate effect is the failure.

This is the trade retries make. Retrying is one of the most effective ways to survive a transient network blip, a busy service or a momentary timeout, but it rests on an assumption that is easy to leave unexamined: that running the action again is harmless. When the action creates something, sends something, charges something or moves a physical thing, a second attempt is not a repeat of a failed attempt. It is a second effect.

Idempotent is about the intended effect, not the request count

The useful word here is idempotent. RFC 9110 defines an idempotent method as one where the intended effect on the server of multiple identical requests is the same as the effect of a single request. It is a statement about the result, not about how many times the request arrived. The standard names PUT, DELETE and the safe methods as idempotent, and it draws the practical conclusion: because repeating them has the same intended effect, they can be retried automatically after a communication failure, while a non-idempotent request should not be retried automatically unless the client has another way to know it is safe.

That last clause is the part most home-grown automation skips. A scheduler, a webhook handler, an agent tool loop and a cloud function are all clients of something. If they retry blindly, they have decided the action is safe to repeat without checking.

An operation identifier is not a whole solution

The common first fix is to give each operation a stable identifier and let the receiver recognise a repeat. Stripe's idempotency keys work this way: the first request's status and body are stored against the key, and later requests with the same key return the same result, including errors. AWS uses a client token, and an AWS SDK or CLI will generate one for you so a retry under the hood still honours an "at most once" commitment.

Both services also compare the incoming request with the original. AWS stores the parameters used for the first call and returns a validation error when a reused identifier carries a different parameter combination; Stripe errors when the same key is reused with different parameters. That comparison is the practical equivalent of a request fingerprint, and it protects you from a key that has drifted away from its meaning.

An identifier alone, though, only answers "have I seen this before?" It does not answer "is the world already in the state I want?" A receiver that never saw the original request, because it was lost before arrival, still has no key to match. And a duplicate that arrives while the original is still running is a different case from one that arrives after it finished: the first should generally wait or be refused rather than run the work twice. Which of those is right is a design judgment, not something a source settles for you.

Four things to settle before the retry runs

Treat this as a short design checklist for any action a bounded agent, routine, webhook or automation may repeat.

  • **A stable operation identifier, created once.** Generate it when the intent is formed, not when the request is sent. A random identifier such as a UUID is a reasonable default, and it must not be reused for a different payload. Never build it from sensitive data such as an email address.
  • **A precondition or state check.** Before acting, ask whether the desired end state is already true, or whether a conflicting operation is in flight. This is what makes a "create if absent" action idempotent even when the receiver never stored a key.
  • **A recorded outcome, with a retention window.** Store what happened, keyed by the identifier, so a retry can return the same answer instead of doing the work again. That record cannot live forever; Stripe prunes keys after at least 24 hours, and AWS discusses the limits of retaining idempotent request history. Decide what happens after expiry rather than discovering it.
  • **A bounded retry policy.** Cap the attempts, back off between them, and treat a validation error as a stop rather than a reason to try again. RFC 9110 adds a subtle rule worth copying: a client should not automatically retry a failed automatic retry.

A worked example: the morning briefing

Suppose a small agent runs each weekday morning and posts a summary to a household chat. The dangerous version retries on any error, so a lost response produces two identical posts. The idempotent version forms an operation identifier for the day's briefing before it sends anything, then checks a small record: has today's briefing already been posted? If yes, it stops and reports the earlier result. If no, it sends, then records the outcome against the identifier. If two runs overlap, the second sees the first in progress and waits rather than duplicating.

The same discipline appears in Home Assistant, though under different names. An automation's run mode decides what a fresh trigger does while a previous run is still going: single refuses the new run and warns, restart stops the old run and starts again, queued runs after the current one completes, and parallel starts another copy. Choosing the mode is choosing the retry semantics, and the default single mode is deliberately conservative for exactly this reason.

What this still cannot promise

No combination of keys, checks and records gives you universal exactly-once execution. A duplicate can still slip through at the edges. A concurrent request can arrive before the first has recorded its outcome. A key can be pruned and then reused. A late retry can arrive after the resource it named has been deleted, and different services make different, defensible choices about what to return then. Two systems with different clocks and different retention windows will disagree about what "already seen" means.

What idempotency buys is narrower and still valuable: it makes the common transient failure safe to retry, and it moves the remaining uncertainty into places you can name, test and monitor. None of these sources promise correctness for your particular action, and a vendor's documented behaviour describes that vendor, not a universal rule.

What we would do next

Pick one action your automation repeats — a notification, a file write, a device command, a webhook callback. Write down its identifier, its precondition check, where its outcome is recorded, how long that record lives, and the maximum number of attempts. If any of those five lines is blank, the retry is currently a bet that the first attempt failed. Make the action safe to repeat before you let it repeat.

Sources and limits

This article synthesises the sources below into a practical explanation. It is not a security standard, legal advice, or a guarantee that guidance current at review time still applies — check the review date above against your own situation.

  1. RFC 9110: HTTP Semantics — Idempotent MethodsIETF. Defines an idempotent method as one where multiple identical requests have the same intended effect as a single request, names PUT, DELETE and the safe methods as idempotent, and explains that idempotent requests can be repeated after a communication failure while non-idempotent requests should not be retried automatically without another way to know that is safe.
  2. Making retries safe with idempotent APIsAWS Builders' Library. Explains the "at most once" contract, unique client request identifiers such as ClientToken, semantically equivalent retry responses, a validation error when a reused identifier carries different parameters, late-arriving retries, and the cost of that extra rigour.
  3. Idempotent requestsStripe. Documents saving the first request's status and body for an idempotency key so retries return the same result including errors, a 255-character key limit, pruning after at least 24 hours, and an error when the same key is reused with different parameters.
  4. Automation modesHome Assistant. Documents the single, restart, queued and parallel run modes for automations, the default queue limit of 10, and the warning emitted when a new run is refused or a limit is exceeded.
  5. Retry pattern — Azure Architecture CenterMicrosoft. Advises considering whether an operation is idempotent before retrying, and describes the failure where a service processes a request successfully but the response is lost, so a retry can execute the operation more than once with unintended side effects.
  • 2026-09-21

    A missing sensor reading is not zero

    Unknown, unavailable and stale sensor values are not measurements. A practical way to keep missing data out of averages, thresholds and automations, and to leave a household with a safe manual path.

  • 2026-07-22

    Make home automations fail safe first

    A practical checklist for designing Home Assistant automations that recover clearly, leave evidence and avoid doing the wrong thing twice.

Share this article

0 views · 0 share actions

Community comments

Comments are reviewed before publication. Keep discussion constructive: no harassment, hate, threats, doxxing, spam, illegal material, or attempts to evade moderation.

No approved comments yet.

Sign in to join the discussion.