Introduction
Ansible Lint rule 505 (missing-import) flags playbooks and roles that reference files, tasks, or roles that cannot be found on the Ansible controller. This is a syntax-check level rule — meaning it catches errors that would cause your playbook to fail at runtime before you even run it.
This article covers the root causes of this error, every common scenario that triggers it, how to fix each one, and best practices for organizing imports in Ansible projects.
Understanding the Error
When ansible-lint encounters a reference to a file that does not exist, it reports:
WARNING Listing 1 violation(s) that are fatal
syntax-check[missing-file]: Unable to retrieve file contents
playbook.yml:5 Could not find or access 'tasks/missing.yml' on the Ansible Controller.
This rule is tagged as core and unskippable — you cannot disable it because a missing file will always cause a runtime failure.
Common Causes
1. Typo in Filename or Path
The most frequent cause — a simple misspelling in the import path:
# Error: filename misspelled
- name: Include setup tasks
ansible.builtin.import_tasks: taks/setup.yml # should be tasks/setup.yml
2. File Does Not Exist Yet
Referencing a task file you planned to create but have not yet written:
# Error: file not created
- name: Include monitoring tasks
ansible.builtin.include_tasks: tasks/monitoring.yml # file doesn't exist
3. Wrong Relative Path
Ansible resolves relative paths differently depending on context:
# From a role's tasks/main.yml:
- ansible.builtin.import_tasks: setup.yml # looks in roles/myrole/tasks/
- ansible.builtin.import_tasks: ../files/setup.yml # wrong - traverses up
# From a playbook:
- ansible.builtin.import_tasks: tasks/setup.yml # looks relative to playbook dir
4. Missing Role in requirements.yml
When a playbook references a role that is not installed:
# Error: role not installed
- hosts: all
roles:
- geerlingguy.docker # not in roles/ and not installed via galaxy
5. Case-Sensitive Filenames
On case-sensitive filesystems, Tasks/Setup.yml is different from tasks/setup.yml:
# Error on Linux (case-sensitive)
- ansible.builtin.import_tasks: Tasks/Setup.yml # actual file: tasks/setup.yml
Error Examples
Example 1: Missing include File
Playbook:
---
- name: Example playbook
hosts: all
tasks:
- name: Include non-existing tasks
ansible.builtin.include_tasks: 'non-existing.yml'
Lint Output:
$ ansible-lint playbook.yml
WARNING Listing 1 violation(s) that are fatal
syntax-check[missing-file]: Unable to retrieve file contents
playbook.yml:1:1 Could not find or access 'non-existing.yml' on the Ansible Controller.
Rule Violation Summary
count tag profile rule associated tags
1 syntax-check[missing-file] min core, unskippable
Failed: 1 failure(s), 0 warning(s) on 1 files.
Example 2: Missing import_playbook
---
- name: Main playbook
hosts: localhost
tasks:
- name: dummy task
ansible.builtin.debug:
msg: "hello"
- ansible.builtin.import_playbook: site-cleanup.yml # file doesn't exist
Lint Output:
syntax-check[missing-file]: Unable to retrieve file contents
main.yml:9 Could not find or access 'site-cleanup.yml' on the Ansible Controller.
Example 3: Missing Role
---
- name: Deploy application
hosts: webservers
roles:
- common
- deploy_app # role directory doesn't exist
Lint Output:
syntax-check[missing-file]: the role 'deploy_app' was not found
Fixing the Error
Fix 1: Create the Missing File
The most straightforward fix — create the file that is being referenced:
# Create the missing task file
mkdir -p tasks
cat > tasks/setup.yml << 'EOF'
---
- name: Ensure packages are installed
ansible.builtin.package:
name: "{{ item }}"
state: present
loop:
- vim
- curl
- wget
EOF
Fix 2: Correct the Path
Verify the relative path is correct for your project structure:
project/
├── playbook.yml
├── tasks/
│ └── setup.yml
└── roles/
└── webserver/
└── tasks/
└── main.yml
# From playbook.yml:
- ansible.builtin.import_tasks: tasks/setup.yml # ✅ correct
# From roles/webserver/tasks/main.yml:
- ansible.builtin.import_tasks: install.yml # ✅ looks in roles/webserver/tasks/
Fix 3: Install Missing Roles
# Install from Galaxy
ansible-galaxy install geerlingguy.docker
# Or install all roles from requirements
ansible-galaxy install -r requirements.yml
# Or install collections
ansible-galaxy collection install -r requirements.yml
Fix 4: Use Conditional Includes
If a file might not exist in all environments, use include_tasks with when:
- name: Check if optional tasks file exists
ansible.builtin.stat:
path: "{{ playbook_dir }}/tasks/optional.yml"
register: optional_tasks
delegate_to: localhost
- name: Include optional tasks if they exist
ansible.builtin.include_tasks: tasks/optional.yml
when: optional_tasks.stat.exists
import_tasks vs include_tasks Behavior
The error manifests differently depending on which directive you use:
| Directive | When Resolved | Missing File Behavior |
|---|---|---|
import_tasks | At playbook parse time (static) | Fails immediately at parse time |
include_tasks | At runtime (dynamic) | Fails when the task is reached |
import_playbook | At playbook parse time (static) | Fails immediately at parse time |
import_role | At playbook parse time (static) | Fails immediately at parse time |
include_role | At runtime (dynamic) | Fails when the task is reached |
Static imports (import_*) are caught by ansible-lint because the file must exist at parse time. Dynamic includes (include_*) are also flagged because they would fail at runtime.
Best Practices for Organizing Imports
1. Use a Consistent Directory Structure
project/
├── ansible.cfg
├── inventory/
│ ├── production
│ └── staging
├── playbooks/
│ ├── site.yml
│ └── deploy.yml
├── tasks/
│ ├── common.yml
│ └── security.yml
└── roles/
└── webserver/
├── tasks/
│ ├── main.yml
│ ├── install.yml
│ └── configure.yml
├── handlers/
│ └── main.yml
├── templates/
└── defaults/
└── main.yml
2. Pin Role Versions in requirements.yml
# requirements.yml
roles:
- name: geerlingguy.docker
version: "6.1.0"
- name: geerlingguy.nginx
version: "3.2.0"
collections:
- name: community.general
version: ">=7.0.0"
3. Validate Imports in CI/CD
Add ansible-lint to your CI pipeline:
# .github/workflows/lint.yml
- name: Run ansible-lint
run: |
pip install ansible-lint
ansible-lint playbooks/ roles/
4. Use Descriptive Filenames
# ✅ Clear and descriptive
- ansible.builtin.import_tasks: tasks/install-nginx.yml
- ansible.builtin.import_tasks: tasks/configure-ssl.yml
# ❌ Vague
- ansible.builtin.import_tasks: tasks/setup.yml
- ansible.builtin.import_tasks: tasks/do-stuff.yml
5. Document External Dependencies
Add a README listing required roles and collections:
## Dependencies
Install before running:
ansible-galaxy install -r requirements.yml
Troubleshooting Checklist
When you encounter missing-import:
- Check the filename — is it spelled correctly?
- Check the path — is it relative to the right directory?
- Check file existence — does
ls -la <path>show the file? - Check case sensitivity — does the case match exactly?
- Check role installation — run
ansible-galaxy listto see installed roles - Check collection installation — run
ansible-galaxy collection list - Check ansible.cfg — is
roles_pathconfigured correctly?
Related Articles
Conclusion
Ansible Lint Error 505 (missing-import) is a critical check that ensures all referenced files, tasks, and roles exist before playbook execution. The fix is usually straightforward — create the missing file, correct the path, or install the missing role/collection. By maintaining a well-organized project structure, using descriptive filenames, pinning dependency versions, and running ansible-lint in CI, you can prevent this error from reaching production.