Summary
Original usecase
As collections can not be dependent on an individual roles, and I wanted to re-use an existing standalone role when it's available, but also have an internal very simplified logic when it's not, instead of the failure, I attempted the following workflow:
- Create a block/rescue
- Put a potentially absent role as
include_role: noop_role
- Put in rescue
include_tasks: simplified_noop
- profit
However, I have realized, that block recovery work in an unexpected way for this usecase:
- Host is still marked as
failed
- Tasks for the hosts keep executing
- But not all of them. In case a playbook is consisting of multiple roles, even withing the same collection, second role will not be executed, as the host will be seen as failed.
To have that said, a behavior has slightly changed between 2.16.18 and 2.17.14 - while a second role is still ghosted, host was not marked as "failed", which I think was even more confusing to debug...
Issue Type
Bug Report
Component Name
ansible.builtin.include_role
Ansible Version
~/.virtualenvs/ansible/bin/ansible --version
ansible [core 2.19.8]
config file = None
configured module search path = ['~/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = ~/.virtualenvs/ansible/lib/python3.11/site-packages/ansible
ansible collection location = ~/.ansible/collections:/usr/share/ansible/collections
executable location = ~/.virtualenvs/ansible/bin/ansible
python version = 3.11.15 (main, Mar 3 2026, 09:26:23) [GCC 13.3.0] (~/.virtualenvs/ansible/bin/python3.11)
jinja version = 3.1.4
pyyaml version = 6.0.2 (with libyaml v0.2.5)
Configuration
# ~/.virtualenvs/ansible/bin/ansible-config dump --only-changed -t all
CONFIG_FILE() = None
GALAXY_SERVERS:
OS / Environment
Ubuntu 24.04, Python 3.11.15
Steps to Reproduce
In a simplistic way, this what I have the following layout of a collection:
~/.ansible/collections/ansible_collections$ find noop_ns | sed -e "s/[^-][^\/]*\// |/g" -e "s/|\([^ ]\)/|-\1/"
noop_ns
|-noop_collection
| |-roles
| | |-trigger
| | | |-tasks
| | | | |-main.yml
| | |-ignored
| | | |-tasks
| | | | |-main.yml
| | |-success
| | | |-tasks
| | | | |-main.yml
| |-playbooks
| | |-sample.yml
$ cat noop_ns/noop_collection/roles/trigger/tasks/main.yml
- block:
- ansible.builtin.include_role:
name: standalone_role
rescue:
- debug:
msg: Rescue task
- debug:
msg: Second task
$ cat noop_ns/noop_collection/roles/ignored/tasks/main.yml
- debug:
msg: This task is ignored
cat noop_ns/noop_collection/roles/success/tasks/main.yml
- debug:
msg: This second role is executed still
$ cat noop_ns/noop_collection/playbooks/sample.yml
- hosts: localhost
roles:
- role: noop_ns.noop_collection.trigger
- role: noop_ns.noop_collection.success
- hosts: localhost
roles:
- role: noop_ns.noop_collection.ignored
Expected Results
- Rescued task is not marked as failed
noop_ns.noop_collection.ignored is actually executed
Actual Results
$ ~/.virtualenvs/ansible/bin/ansible-playbook noop_ns.noop_collection.sample
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [localhost] ***************************************************************************************************************************************************************************************************************************
TASK [Gathering Facts] *********************************************************************************************************************************************************************************************************************
ok: [localhost]
TASK [ansible.builtin.include_role : standalone_role] **************************************************************************************************************************************************************************************
[ERROR]: the role 'standalone_role' was not found in noop_ns.noop_collection:ansible.legacy:~/.ansible/collections/ansible_collections/noop_ns/noop_collection/playbooks/roles:~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:~/.ansible/collections/ansible_collections/noop_ns/noop_collection/playbooks
Origin: ~/.ansible/collections/ansible_collections/noop_ns/noop_collection/roles/trigger/tasks/main.yml:3:15
1 - block:
2 - ansible.builtin.include_role:
3 name: standalone_role
^ column 15
fatal: [localhost]: FAILED! => {"changed": false, "reason": "the role 'standalone_role' was not found in noop_ns.noop_collection:ansible.legacy:~/.ansible/collections/ansible_collections/noop_ns/noop_collection/playbooks/roles:~/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:~/.ansible/collections/ansible_collections/noop_ns/noop_collection/playbooks"}
TASK [noop_ns.noop_collection.trigger : debug] *********************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "Rescue task"
}
TASK [noop_ns.noop_collection.trigger : debug] *********************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "Second task"
}
TASK [noop_ns.noop_collection.success : debug] *********************************************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "This second role is executed still"
}
PLAY RECAP *********************************************************************************************************************************************************************************************************************************
localhost : ok=4 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0
Code of Conduct
Summary
Original usecase
As collections can not be dependent on an individual roles, and I wanted to re-use an existing standalone role when it's available, but also have an internal very simplified logic when it's not, instead of the failure, I attempted the following workflow:
include_role: noop_roleinclude_tasks: simplified_noopHowever, I have realized, that block recovery work in an unexpected way for this usecase:
failedTo have that said, a behavior has slightly changed between 2.16.18 and 2.17.14 - while a second role is still ghosted, host was not marked as "failed", which I think was even more confusing to debug...
Issue Type
Bug Report
Component Name
ansible.builtin.include_role
Ansible Version
Configuration
OS / Environment
Ubuntu 24.04, Python 3.11.15
Steps to Reproduce
In a simplistic way, this what I have the following layout of a collection:
$ cat noop_ns/noop_collection/roles/trigger/tasks/main.yml$ cat noop_ns/noop_collection/roles/ignored/tasks/main.ymlcat noop_ns/noop_collection/roles/success/tasks/main.yml$ cat noop_ns/noop_collection/playbooks/sample.ymlExpected Results
noop_ns.noop_collection.ignoredis actually executedActual Results
Code of Conduct