Why error handling?
By default, a failed task can stop the rest of the play for that host. Error-handling keywords let you ignore optional failures, define custom failure rules, and run cleanup/recovery logic.
ignore_errors
Continue even if the task fails:
- name: Optional cleanup
file:
path: /tmp/old-cache
state: absent
ignore_errors: trueUse for optional steps or cleanup — not as a blanket “never fail” switch.
failed_when
Decide yourself when a task counts as failed:
- name: Check nginx
shell: systemctl is-active nginx
register: nginx_status
failed_when: "'inactive' in nginx_status.stdout"
changed_when: falsefailed_when: false means “never fail this task” (useful when you only want the register output).
changed_when
Control whether Ansible reports changed (important for handlers):
- name: Check uptime
command: uptime
changed_when: falseRead-only checks should not look like changes.
block / rescue / always
- block:
- name: Install nginx
apt:
name: nginx
state: present
- name: Start nginx
service:
name: nginx
state: started
rescue:
- name: Report failure
debug:
msg: "Nginx setup failed on {{ inventory_hostname }}"
always:
- name: Finished
debug:
msg: "Playbook section completed"| Section | When it runs |
|---|---|
block | Main tasks |
rescue | Only if a block task fails |
always | Success or failure |
Mini project — safer Nginx deploy
---
- name: Safe Nginx deployment
hosts: all
become: true
tasks:
- block:
- name: Install nginx
apt:
name: nginx
state: present
- name: Validate config exists
stat:
path: /etc/nginx/nginx.conf
register: conf
failed_when: not conf.stat.exists
- name: Start nginx
service:
name: nginx
state: started
enabled: true
rescue:
- name: Capture failure
debug:
msg: "Deploy failed — check packages and config"
always:
- name: Done
debug:
msg: "Deployment attempt finished"Interview: rescue runs only on failure; always runs regardless. Prefer precise failed_when / changed_when over broad ignore_errors.