Master Error Handling In Ansible: The Ultimate Guide for Beginners (2026)

Error Handling in Ansible

Ansible is a handy tool to simplify configuration management and application deployment. However, when running a playbook, some tasks may fail. By default, a task failure halts the playbook execution. But what if you want to continue executing the playbook despite a failure? This is where Error Handling in Ansible becomes useful.

Table of Contents

Objectives

After completing this section, you should be able to control what happens when a task fails, and what conditions cause a task to fail.

How To Manage Error Handling in Ansible?

Ansible evaluates the return code of each task to determine whether the task succeeded or failed. Normally, when a task fails Ansible immediately aborts the rest of the play on that host, skipping all subsequent tasks.

However, sometimes you might want to have play execution continue even if a task fails. For example, you might expect that a particular task could fail, and you might want to recover by running some other task conditionally and this is where Error Handling In Ansible becomes useful. There are a number of Ansible features that can be used to manage task errors.

How To Ignore Task Failure

By default, if a task fails, the play is aborted. However, this behavior can be overridden by ignoring failed tasks. You can use the ignore_errors keyword in a task to accomplish this.

The following example shows how to use ignore_errors in a task to continue playbook execution on the host even if the task fails. For example, if the notapkg package does not exist then the yum module fails, but having ignore_errors set to yes allows execution to continue.

- name: Latest version of notapkg is installed
yum:
name: notapkg
state: latest
ignore_errors: yes

Forcing Execution of Handlers after Task Failure

Normally when a task fails and the play aborts on that host, any handlers that had been notified by earlier tasks in the play will not run. If you set the force_handlers: yes keyword on the play, then notified handlers are called even if the play aborted because a later task failed.

The following example shows hows to use the force_handlers keyword in a play to force execution of the handler even if a task fails:

---
- hosts: all
force_handlers: yes
tasks:
- name: a task which always notifies its handler
command: /bin/true
notify: restart the database
- name: a task which fails because the package doesn't exist
yum:
name: notapkg
state: latest
handlers:
- name: restart the database
service:
name: mariadb
state: restarted

How to Specify Task Failure Conditions ?

You can use the failed_when keyword on a task to specify which conditions indicate that the task has failed. This is often used with command modules that may successfully execute a command, but the command’s output indicates a failure.

For example, you can run a script that outputs an error message and use that message to define the failed state for the task. The following example shows how the failed_when keyword can be used in a task:

tasks:
- name: Run user creation script
shell: /usr/local/bin/create_users.sh
register: command_result
failed_when: "'Password missing' in command_result.stdout
The fail module can also be used to force a task failure. The above scenario can alternatively be written as two tasks
tasks:
- name: Run user creation script
shell: /usr/local/bin/create_users.sh
register: command_result
ignore_errors: yes
- name: Report script failure
fail:
msg: "The password is missing in the output"
when: "'Password missing' in command_result.stdout"

You can use the fail module to provide a clear failure message for the task. This approach also enables delayed failure, allowing you to run intermediate tasks to complete or roll back other changes.

Managing “Changed” Status

When a task makes a change to a managed host, it reports the changed state and notifies handlers. When a task does not need to make a change, it reports ok and does not notify handlers.

The changed_when keyword can be used to control when a task reports that it has changed. For example, the shell module in the following example is being used to get a Kerberos credential which will be used by subsequent tasks. It normally would always report changed when it runs. To suppress that change, changed_when: false is set so that it only reports ok or failed.

- name: get Kerberos credentials as "admin"
shell: echo "{{ krb_admin_pass }}" | kinit -f admin
changed_when: false

Prevent a task from reporting a “change” if nothing actually happened (common with the shell or command modules).

- name: Check version
  ansible.builtin.command: python3 --version
  register: py_version
  changed_when: false  # This task will always report 'OK', never 'Changed'

 The following example uses the shell module to report changed based on the output of the module that is collected by a registered variable:

tasks:
- shell:
cmd: /usr/local/bin/upgrade-database
register: command_result
changed_when: "'Success' in command_result.stdout"
notify:
- restart_database
handlers:
- name: restart_database
service:
name: mariadb
state: restarted

Ansible Blocks and Error Handling

In playbooks, blocks are clauses that logically group tasks, and can be used to control how tasks are executed. For example, a task block can have a when keyword to apply a conditional to multiple tasks.

- name: block example
hosts: all
tasks:
- name: installing and configuring Yum versionlock plugin
block:
- name: package needed by yum
yum:
name: yum-plugin-versionlock
state: present
- name: lock version of tzdata
lineinfile:
dest: /etc/yum/pluginconf.d/versionlock.list
line: tzdata-2016j-1
state: present
when: ansible_distribution == "RedHat"

Blocks also allow for error handling in combination with the rescue and always statements. If any task in a block fails, tasks in its rescue block are executed in order to recover. After the tasks in the block clause run, as well as the tasks in the rescue clause if there was a failure, then tasks in the always clause run. To summarize:

• block: Defines the main tasks to run.

• rescue: Defines the tasks to run if the tasks defined in the block clause fail.

• always: Defines the tasks that will always run independently of the success or failure of tasks defined in the block and rescue clauses.

The following example shows how to implement a block in a playbook. Even if tasks defined in the block clause fail, tasks defined in the rescue and always clauses are executed.

tasks: 
- name: Upgrade DB
block:
- name: upgrade the database
shell:
cmd: /usr/local/lib/upgrade-database
rescue:
- name: revert the database upgrade
shell:
cmd: /usr/local/lib/revert-database
always:
- name: always restart the database
service:
name: mariadb
state: restarted

The when condition on a block clause also applies to its rescue and always clauses if present.

Conclusion: Mastering Error Handling in Ansible

Effective Error Handling in Ansible is what separates a basic script from a professional, production-ready automation workflow. By mastering techniques like ignore_errors, failed_when, and the robust block-rescue structures, you ensure that your Ansible playbooks can recover gracefully from unexpected issues rather than failing blindly.

As you continue to build out your automation tasks, remember that the goal of Error Handling in Ansible isn’t just to bypass errors, but to manage them predictably. Implementing these strategies will result in more resilient infrastructure and significantly less time spent manually debugging failed jobs.

You can refer also Ansible public documentation page https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_error_handling.html to know  more about Error Handling in Ansible .

Please visit our official website https://linuxgktech.com/ansible/ to know more about Ansible

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top