The Problem
Splitting logic into multiple files keeps your Ansible roles clean and manageable. You likely use include_tasks or import_tasks to pull these snippets into your main.yml. But even when the file is clearly sitting in your directory, Ansible sometimes throws a 'Could not find or access' error that halts your automation.
The error usually looks like this:
ERROR! Could not find or access '/path/to/tasks/setup.yml' on the Ansible Controller.
If we are using a role, the lookup is relative to the role's tasks/ directory.
Make sure the file exists and is readable.
Why This Happens
Ansible follows a strict search hierarchy. When executing inside a role, the engine automatically searches for task files within that role's tasks/ subdirectory. Most failures stem from four specific oversights:
- Redundant paths: Manually adding "tasks/" to a path when Ansible is already looking there.
- Execution context: Running a playbook from a root directory that breaks relative path lookups.
- Case sensitivity: Linux sees
Setup.ymlandsetup.ymlas two different files. - Broken symlinks: Using links that point to non-existent targets on the controller.
Step-by-Step Fixes
1. Remove Redundant Directory Prefixes
Imagine your file structure looks like this:
roles/
my_app/
tasks/
main.yml
setup.yml
Inside main.yml, you might mistakenly write:
# INCORRECT - This results in searching for tasks/tasks/setup.yml
- name: Include setup tasks
include_tasks: tasks/setup.yml
Since Ansible already targets the tasks/ folder, adding it again creates a non-existent path. Fix this by using the filename alone:
# CORRECT
- name: Include setup tasks
include_tasks: setup.yml
2. Use the {{ role_path }} Magic Variable
Hardcoding paths is risky in complex projects. If you are calling tasks from a different role or dealing with nested includes, use the role_path variable. This forces an absolute path on the controller, leaving no room for ambiguity.
- name: Include setup from current role safely
include_tasks: "{{ role_path }}/tasks/setup.yml"
This approach is bulletproof when your playbooks live in non-standard directory structures.
3. Verify Extensions and Typos
Ansible won't guess your file extension. If your file is setup.yaml but your code references setup.yml, the task will fail immediately. Run a quick check to see exactly what is on the disk:
ls roles/my_app/tasks/
Standardize your project on one extension—either .yml or .yaml—to prevent these 3:00 AM debugging sessions.
4. Check Filesystem Permissions
Sometimes the file exists, but Ansible lacks the 'access' part of the error message. The user running ansible-playbook must have read permissions for the file and execute permissions for every parent directory. Fix permissions with these commands:
# Set standard read permissions (644) and directory access (755)
chmod 644 roles/my_app/tasks/setup.yml
chmod 755 roles/my_app/tasks/
5. Static vs. Dynamic Task Loading
While import_tasks and include_tasks look similar, they behave differently. import_tasks is static; it is processed when the playbook is first parsed. include_tasks is dynamic and evaluated at runtime. If your path uses a variable that isn't defined until the play starts, import_tasks will fail.
# This works because it evaluates during execution
- name: Dynamic include
include_tasks: "{{ os_family }}_tasks.yml"
# This often fails if os_family is a gathered fact
- name: Static import
import_tasks: "{{ os_family }}_tasks.yml"
Verification
To see exactly where Ansible is looking, run your playbook with triple verbosity. This reveals the search path logic in real-time:
ansible-playbook site.yml -vvv
Search the output for lines starting with searching for.... If you see a path like .../tasks/tasks/setup.yml, you've found your redundant directory bug.
Pro Tips
- Avoid deep nesting: If a role requires more than 5 or 6 task files, it might be doing too much. Consider breaking it into two smaller, specialized roles.
- Let the linter help: Use
ansible-lint. It catches pathing issues and deprecated syntax before you even run the code. - The 1-second check: Always verify that your
main.ymland sub-tasks are in the sametasks/folder. 90% of these errors are simple placement mistakes.

