An automation that works when you test it and then misses one evening in three is rarely a bug in Home Assistant. Most of these failures come from a handful of rules about how automations start, what counts as a change, what happens when an automation is triggered while it is still running, and what a restart does to a run in progress. Once you know them, most "random" failures turn out to be predictable.

An automation starts on a change, not while something is true

Home Assistant's documentation sums it up in two sentences: an automation starts when something changes, not while something is true, and its conditions are checked once, right after a trigger reacts.

That explains a lot of surprises:

  • A door-opened trigger does not react if the door is already open when you save the automation. It waits until the door closes and opens again.
  • A temperature trigger set to above 25 °C does nothing if the room is already at 26 °C. It reacts only after the value drops to 25 °C or below and then rises again.
  • A condition that is false when the trigger fires stops the run, even if it becomes true a minute later. Conditions never start an automation.

The documentation's example: you want the living room lights on when Paulus is home and the sun has set. With the trigger "Paulus arrives" and the condition "after sunset", nothing happens if he gets home in daylight and stays. The fix is to give every change its own trigger and to check every situation as a condition:

triggers:
  - trigger: state
    entity_id: person.paulus
    to: "home"
  - trigger: sun
    event: sunset
conditions:
  - condition: state
    entity_id: person.paulus
    state: "home"
  - condition: sun
    after: sunset
actions:
  - action: light.turn_on
    target:
      entity_id: light.living_room

Now whichever happens second starts the automation, and the conditions confirm the first one is still true. If the automation should also catch up after a restart, add one more trigger: trigger: homeassistant with event: start. The conditions decide whether there is anything to do.

What counts as a change

Not every update starts a trigger, and some updates start one when you do not expect it.

Attribute changes. A State changed trigger with no From or To fires on every change of the entity, including attributes such as the brightness of a light or a sensor's battery level. If you only care about the state, set To to Any state (ignoring attribute changes). In YAML that is to: null, with the key present and the value empty.

Coming back from unavailable. When a device drops off the network and returns, its state goes from unavailable to, say, on. A State changed trigger with from: "off" does not match that, so the automation stays silent. Remove the From state if the previous state does not matter. Most of the purpose-built triggers, such as Light turned on, do not react to a recovery from unavailable at all. The State changed trigger does.

Raw states. The UI shows a door sensor as "Open" or a motion sensor as "Detected", but the trigger compares the raw state, which is on. Pick the state from the list in the editor or check the raw value in Settings > Tools > States.

Setting the same value again normally does not produce a state change, so nothing fires.

Flapping sensors and "For at least"

A motion sensor that flickers between detected and clear, or a power reading hovering around a threshold, can start an automation every few seconds. The usual cure is For at least (for:) on the trigger: the change counts only after the new state has lasted that long.

Two things to know. The timer starts over whenever the entity leaves the state, so a sensor that briefly detects motion again restarts a 10 minute wait on "clear". And the wait does not survive a restart of Home Assistant or a reload of automations. A State condition with For at least has a similar catch: after a restart, Home Assistant counts the time from the moment it started, not from the real change.

Pick the right mode

Sometimes an automation is triggered again while it is still running, typically while it sits in a delay. The mode decides what happens:

  • Single (default): the new start is ignored, and the log gets an "Already running" warning.
  • Restart: the current run stops, including any delay or wait, and a new one starts from the top, provided the conditions pass.
  • Queued: the new run waits until earlier runs have finished. Runs start in order.
  • Parallel: a new, independent run starts next to the existing ones.

For Queued and Parallel, the maximum number of runs defaults to 10 (minimum 2). Change the mode in the editor under Menu > Change mode, or with mode: and max: in YAML.

Each mode fits a typical job. A motion light wants Restart, because every new motion should reset the countdown:

mode: restart
triggers:
  - trigger: state
    entity_id: binary_sensor.hallway_motion
    to: "on"
actions:
  - action: light.turn_on
    target:
      entity_id: light.hallway
  - wait_for_trigger:
      - trigger: state
        entity_id: binary_sensor.hallway_motion
        to: "off"
  - delay: "00:02:00"
  - action: light.turn_off
    target:
      entity_id: light.hallway

A throttled notification wants Single with a delay at the end. The documentation's example runs at most once every 5 minutes and silences the warning for skipped starts with max_exceeded: silent. Queued suits a device that cannot handle several commands at once. Parallel fits a notification that should go out for every single event.

The same mode can bite in the wrong place. If a trace shows a run that stopped mid-delay at the exact moment a new run started, that is Restart mode doing its job. If the delay should finish, use Single.

Restarts wipe runs in progress

When Home Assistant restarts, for an update for example, every running automation stops. A delay, a wait or a For at least that was counting down is lost, and the automation does not resume afterwards. Runs also stop when you save a changed automation (other automations keep running), when you turn the automation off, or when you change its entity ID.

A two-minute delay rarely matters. A three-hour one will eventually overlap with an update. The documentation suggests two replacements:

  • For a fixed time of day, use a Time trigger instead of waiting.
  • For a duration, use a timer helper. Create it in Settings > Devices & services > Helpers > Create helper > Timer and turn on Restore state and time when Home Assistant starts. A restored timer keeps its remaining time across a restart. Start it in one automation and react to the Timer finished trigger in another.

This works when the automation waits only once at a time and the actions after the wait do not need trigger data, such as which sensor started it. One gap remains: if the timer runs out while Home Assistant is off, the Timer finished trigger does not fire after startup. Cover it with a Home Assistant start trigger. Here is a bathroom fan that runs for 20 minutes after the light goes off:

alias: "Bathroom fan: start"
triggers:
  - trigger: state
    entity_id: light.bathroom
    to: "off"
actions:
  - action: fan.turn_on
    target:
      entity_id: fan.bathroom
  - action: timer.start
    target:
      entity_id: timer.bathroom_fan
    data:
      duration: "00:20:00"
alias: "Bathroom fan: stop"
triggers:
  - trigger: timer.finished
    target:
      entity_id: timer.bathroom_fan
  - trigger: homeassistant
    event: start
conditions:
  - condition: state
    entity_id: timer.bathroom_fan
    state: "idle"
actions:
  - action: fan.turn_off
    target:
      entity_id: fan.bathroom

A timer is idle when it has finished, was canceled or was never started, and active while it counts down. After a restart, a restored timer with time left is active again, so the condition stops the second automation and the fan keeps running until the timer finishes. A timer that ran out during the restart is idle, and the start trigger turns the fan off. The cost: a fan someone switched on by hand will also be turned off at the next restart.

Waits that never end, or end too soon

The two wait blocks behave in opposite ways:

  • Wait for a trigger reacts only to a change that happens after the wait starts. If the door is already closed, waiting for "door closed" never finishes.
  • Wait for a template continues immediately if the template is already true.

Both accept a timeout, and Continue on timeout is on by default, so after the timeout the automation carries on as if the wait had succeeded. Turn it off to stop instead, or check wait.completed in an If-then block: it is false when the timeout ended the wait.

Several triggers, one automation

Instead of three automations for one device, give one automation several triggers and branch with the Triggered by condition inside a Choose block. The editor creates the trigger IDs for you. In YAML, set id: on each trigger and use condition: trigger with that ID. Without an ID, a trigger is identified by its position, starting at 0, while the editor numbers triggers from 1.

Watch for duplicate runs. A trigger on a light and another on the group containing it both fire when the light turns on, and each starts the automation. The top of each trace shows which trigger started it.

Use the trace, and test with real trigger data

Each automation keeps a trace of its last 5 runs by default. A trace shows the trigger, every condition result and what each action did. The run list says how it ended, for example "Stopped because a condition failed". To keep more, add this in YAML:

trace:
  stored_traces: 20

Automations written in YAML need an id for traces to be available.

Be careful with Run actions. It skips triggers and conditions and runs without trigger data, so a Triggered by condition never passes and templates that use trigger have nothing to read. To test with real trigger data, simulate the change: set the entity's state in Settings > Tools > States or fire the event in Settings > Tools > Events. That starts every automation listening for that change, and their actions control real devices, so check what else reacts before you press the button.