Skip to content

Role search path implicitly includes working directory of ansible-playbook command #87100

@yump

Description

@yump

Summary

This bug is similar to, but not quite like, #75120 and #17894.

The documentation says roles are searched for in 4 places:

  1. collections,
  2. ./roles/ in the directory containing the playbook file,
  3. In roles_path
  4. directly next to the playbook file

Additionally, they will also be picked up from a 5th place: the working directory where ansible-playbook is run.

And, if a role cannot be found, the error message's list of places the role was not found DOES NOT list the current working directory, despite the fact that it is searched.

I discovered this when (with roles_path = ./roles in ansible.cfg) I moved my playbooks into a ./playbooks subdir, and then accidentally ran ansible-galaxy role init blah instead of ansible-galaxy role init roles/blah.

According to an LLM, and verified by me reading the code, the issue arises when the role loader falls back to unfrackpath(role_name). The basedir argument defaults to None, which results in searching the current directory.

Issue Type

Bug Report

Component Name

RoleDefinition

Ansible Version

$ ansible --version
ansible [core 2.20.5]
  config file = /home/rhaley/.ansible.cfg
  configured module search path = ['/home/rhaley/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3.14/site-packages/ansible
  ansible collection location = /home/rhaley/.ansible/collections:/usr/share/ansible/collections
  executable location = /usr/bin/ansible
  python version = 3.14.4 (main, Apr 16 2026, 00:00:00) [GCC 16.0.1 20260321 (Red Hat 16.0.1-0)] (/usr/bin/python3)
  jinja version = 3.1.6
  pyyaml version = 6.0.3 (with libyaml v0.2.5)

Configuration

# if using a version older than ansible-core 2.12 you should omit the '-t all'
$ ansible-config dump --only-changed -t all
ANSIBLE_NOCOWS(env: ANSIBLE_NOCOWS) = True
CONFIG_FILE() = /home/rhaley/.ansible.cfg
EDITOR(env: EDITOR) = /usr/bin/vim
INTERPRETER_PYTHON(/home/rhaley/.ansible.cfg) = auto

OS / Environment

Fedora 44. Also reproduces in the ansible-dev-tools dev container.

Steps to Reproduce

#!/usr/bin/env bash

workspace="/tmp/ansible_bug"
[ -x "$workspace" ] && rm -r "$workspace"
mkdir -p "$workspace"
cd "$workspace"

setup () {

    # test playbook
    mkdir -p playbooks
    cat >playbooks/test.yml <<EOF
---
- name: Play to reproduce bug
  hosts: localhost
  roles:
    - testrole
EOF

    # role in unorothodox location
    mkdir -p roles_dir/testrole/tasks/
    cat >roles_dir/testrole/tasks/main.yml <<EOF
---
- ansible.builtin.debug:
    msg: "this should not work"
EOF
}

reproduce () {
    set -x
    # should error, does
    cd "$workspace"
    ansible-playbook "$workspace/playbooks/test.yml"

    # should error, DOES NOT
    cd "$workspace/roles_dir"
    ansible-playbook "$workspace/playbooks/test.yml"
}

setup
reproduce

Expected Results

Ideally, the role would not be found in either case. If I accidentally created the role in the wrong place, I don't want that covered up because I just happened to run ansible-playbook in the same directory as the role.

Baring that, the documentation would list the current working directory as one of the places searched for roles.

Actual Results

+ cd /tmp/ansible_bug
+ ansible-playbook /tmp/ansible_bug/playbooks/test.yml
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
[ERROR]: the role 'testrole' was not found in /tmp/ansible_bug/playbooks/roles:/home/rhaley/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles:/tmp/ansible_bug/playbooks
Origin: /tmp/ansible_bug/playbooks/test.yml:5:7

3   hosts: localhost
4   roles:
5     - testrole
        ^ column 7

+ cd /tmp/ansible_bug/roles_dir
+ ansible-playbook /tmp/ansible_bug/playbooks/test.yml
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [Play to reproduce bug] *****************************************************************************************************

TASK [Gathering Facts] ***********************************************************************************************************
ok: [localhost]

TASK [testrole : ansible.builtin.debug] ******************************************************************************************
ok: [localhost] => {
    "msg": "this should not work"
}

PLAY RECAP ***********************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Code of Conduct

  • I agree to follow the Ansible Code of Conduct

Metadata

Metadata

Assignees

No one assigned

    Labels

    affects_2.20bugThis issue/PR relates to a bug.has_prThis issue has an associated PR.needs_triageNeeds a first human triage before being processed.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions