Most module not found, collection not found, and undefined variable messages share one root cause: Ansible cannot resolve a name at runtime—a module, collection path, inventory variable, role default, or Jinja2 expression.
This guide fixes resolution errors: FQCN and collection installs, path mismatches, requirements.yml in CI, execution environments, and variable scope in inventory, roles, and templates. If you need to find which troubleshooting layer failed first, use the Ansible troubleshooting hub. To inspect values with debug and register, see debug Ansible playbooks.
Tested on: Rocky Linux 10.2 (Red Quartz); kernel 6.12.0-211.16.1.el10_2.0.1.x86_64; ansible-core 2.16.16.
~/ansible-project, inventory group lab, and playbooks in playbooks/. Use your own host names and paths if yours differ.
What These Ansible Errors Mean
These errors appear when Ansible looks up a name and finds nothing usable:
| Error family | What Ansible cannot resolve |
|---|---|
| Collection error | Collection missing or not in the search path |
| Module error | Wrong module name, missing collection, or ambiguous short name |
| Undefined variable | Variable never defined or not visible in current scope |
| Template variable error | Jinja2 references a missing variable during rendering |
The fix always starts with the exact error text, then the name Ansible failed to resolve.
First-Line Checks for Module and Collection Errors
Run these two commands before you edit playbook tasks or reinstall collections. They answer whether the collection is on disk and whether the module name you typed is valid.
List installed collections and versions:
ansible-galaxy collection listSample output:
# /usr/share/ansible/collections/ansible_collections
Collection Version
------------- -------
ansible.posix 2.1.0Confirm the module FQCN exists and read its options (-t module limits ansible-doc to modules):
ansible-doc -t module ansible.builtin.copyIf ansible-galaxy collection list does not show the namespace (for example community.general), install or fix collections_path before chasing YAML. If the collection is listed but ansible-doc -t module namespace.collection.module fails, the FQCN in the task is wrong—not the install.
Quick Difference: Collection, Module and Variable Errors
| Error | Usually means | First check |
|---|---|---|
couldn't resolve module/action |
Typo, missing collection, wrong FQCN | Module name and ansible-galaxy collection install |
collection ... was not found |
Collection not installed in current path | ansible-galaxy collection list |
The module ... was not found in configured module paths |
Module missing or wrong namespace | FQCN and ansible-doc |
'myvar' is undefined |
Variable not in play, inventory, role, or vars file | Variable location and ansible-inventory --host |
AnsibleUndefinedVariable |
Jinja2 expression references missing variable | Define variable or debug template inputs |
template error while templating string |
Jinja2 syntax or missing variable | Print inputs before the template task |
Common Error Messages Covered in This Guide
| Error message | Common cause | Start here |
|---|---|---|
couldn't resolve module/action |
Typo, missing collection, wrong FQCN | Check module name and install collection |
The module ... was not found in configured module paths |
Module missing or wrong namespace | Use FQCN and ansible-doc |
collection ... was not found |
Collection not installed where Ansible runs | ansible-galaxy collection list |
unable to find collection |
Wrong collection path or execution environment | Check COLLECTIONS_PATHS |
'my_variable' is undefined |
Variable not defined or wrong scope | Inventory, group_vars, role defaults |
dict object has no attribute |
Wrong nested key or missing dictionary value | Debug the parent dictionary |
AnsibleUndefinedVariable |
Variable expression failed during template or task | Define variable or use safe default |
template error while templating string |
Jinja2 expression or missing variable | Debug template inputs |
Fix couldn't resolve module/action Error
This parse-time error means Ansible cannot map the task module name to an installed module—often a typo, a collection that is not installed, or a wrong FQCN.
Ambiguous or wrong namespace:
- name: Manage SELinux file context
nonexistent.collection.sefcontext:
target: '/srv/www/html(/.*)?'
setype: httpd_sys_content_t
state: presentSample output:
ERROR! couldn't resolve module/action 'nonexistent.collection.sefcontext'. This often indicates a misspelling, missing collection, or incorrect module path.Use the correct FQCN and install the collection first:
- name: Manage SELinux file context
community.general.sefcontext:
target: '/srv/www/html(/.*)?'
setype: httpd_sys_content_t
state: presentConfirm the module exists on the control node:
cd ~/ansible-projectansible-doc community.general.sefcontextSample output:
> COMMUNITY.GENERAL.SEFCONTEXT (/home/ansible/.ansible/collections/ansible_collections/community/general/plugins/modules/sefcontext.py)
Manages SELinux file context mapping definitions. Similar to
the `semanage fcontext' command.If ansible-doc cannot find the module, the collection is missing or not visible in the current collection paths.
Fix Ansible Collection Not Found Errors
Install a collection on the control node:
ansible-galaxy collection install community.generalList installed collections and versions:
ansible-galaxy collection listSample output:
# /home/ansible/.ansible/collections/ansible_collections
Collection Version
------------------------ -------
community.crypto 2.22.0
community.general 9.5.2
# /usr/share/ansible/collections/ansible_collections
Collection Version
------------------------ -------
ansible.posix 2.1.0
redhat.rhel_system_roles 1.120.5Filter to one namespace:
ansible-galaxy collection list community.generalPin versions in requirements.yml and install from the project directory. Pin to the version you tested—open ranges can pull a collection that requires a newer ansible-core:
---
collections:
- name: community.general
version: "9.5.2"If you need a range, cap the upper bound so Galaxy cannot install a collection built for a newer core than yours—for example ">=8.0.0,<10.0.0" with ansible-core 2.16.x, not an unbounded ">=8.0.0".
I pinned community.general 9.5.2 with ansible-core 2.16.16 for these runs. If you upgrade ansible-core, check the collection docs and pin a version that matches your runtime.
ansible-galaxy collection install -r requirements.ymlSample output:
Starting galaxy collection install process
Nothing to do. All requested collections are already installed. If you want to reinstall them, consider using `--force`.For collection concepts and RHEL System Roles, see Ansible collections. On air-gapped hosts, point ansible-galaxy collection install at a tarball or private Galaxy mirror instead of galaxy.ansible.com.
Fix Ansible Module Not Found Errors
| Cause | Fix |
|---|---|
| Typo in module name | Check spelling with ansible-doc <fqcn> |
| Module moved to a collection | Use FQCN and install the collection |
| Collection not installed | ansible-galaxy collection install namespace.collection |
| Wrong environment | Install on the control node, CI runner, or execution environment image |
| Old ansible-core version | Confirm module availability for your version |
| Short name conflict | Use FQCN |
When the module name is completely wrong, ansible-doc warns immediately:
ansible-doc nonexistent.moduleSample output:
[WARNING]: nonexistent.module was not foundDuring a play, Ansible may report:
fatal: [rocky2]: FAILED! => {"msg": "The module nonexistent.module was not found in configured module paths"}Use -t when checking a plugin type other than the default module lookup—module, filter, and lookup resolution failures look similar:
ansible-doc -t module community.general.sefcontextansible-doc -t lookup ansible.builtin.fileSample output:
> ANSIBLE.BUILTIN.FILE (/usr/lib/python3.12/site-packages/ansible/plugins/lookup/file.py)
This lookup returns the contents from a file on the AnsibleFor Jinja2 filters, use -t filter:
ansible-doc -t filter ansible.builtin.defaultansible-doc -t filter ansible.builtin.mandatoryansible-doc -t filter ansible.builtin.type_debugUse FQCN to Avoid Module Name Conflicts
FQCN (Fully Qualified Collection Name) has the form namespace.collection.module. It tells Ansible exactly which collection provides the module.
| Short name | FQCN | Why |
|---|---|---|
copy |
ansible.builtin.copy |
Built-in module |
template |
ansible.builtin.template |
Built-in module |
sefcontext |
community.general.sefcontext |
External collection |
firewalld |
ansible.posix.firewalld |
Collection-provided module |
A short name such as sefcontext may work when community.general is installed, but FQCN is explicit and avoids ambiguity across environments. Prefer FQCN in playbooks you share with CI or teammates.
Verify Installed Collections
When a playbook works on your laptop but fails elsewhere, compare collection paths before changing tasks.
Check Ansible version and collection locations:
cd ~/ansible-projectansible --versionSample output:
ansible [core 2.16.16]
config file = /home/ansible/ansible-project/ansible.cfg
ansible collection location = /home/ansible/.ansible/collections:/usr/share/ansible/collectionsShow active config overrides:
ansible-config dump --only-changedSample output:
COLLECTIONS_PATHS(/home/ansible/ansible-project/ansible.cfg) = ['/home/ansible/.ansible/collections', '/usr/share/ansible/collections']
CONFIG_FILE() = /home/ansible/ansible-project/ansible.cfg--only-changed is useful for project overrides. If it does not show collection paths, dump the full config and filter for COLLECTIONS_PATHS:
ansible-config dump | grep -i COLLECTIONS_PATHSample output:
COLLECTIONS_PATHS(/home/ansible/ansible-project/ansible.cfg) = ['/home/ansible/.ansible/collections', '/usr/share/ansible/collections']
GALAXY_COLLECTIONS_PATH_WARNING(default) = TrueThe ansible collection location line and COLLECTIONS_PATHS must include the directory where ansible-galaxy installed the collection.
Fix Wrong Collection Install Path
By default, collections install under ~/.ansible/collections/ansible_collections/. Failures often come from user, directory, or config mismatches.
| Symptom | Cause | Fix |
|---|---|---|
| Works as user A, fails as user B | Collection in user A's home only | Install project-local or system-wide |
| Works locally, fails in CI | CI skipped requirements.yml |
Add install step before ansible-playbook |
collection list shows module, play fails |
Different config at runtime | Compare ansible --version in both shells |
| Collection in repo but not found | Wrong directory layout | Use ansible_collections/namespace/name/ |
Run playbooks from the directory that contains ansible.cfg, or pass -i and config explicitly so COLLECTIONS_PATHS matches where collections were installed. Project-level paths belong in ansible.cfg.
When a collection is “installed” but the play still fails, confirm you are checking the same runtime user and binary as ansible-playbook:
whoamiwhich ansibleansible --versionansible-galaxy collection list community.generalRun these as the same user—or inside the same runner—that executes ansible-playbook. Installing a collection in your interactive shell does not help if CI, AWX, sudo, or an execution environment runs Ansible from a different home directory or Python environment.
Fix requirements.yml Not Installed in CI or New Machines
Commit a requirements.yml beside your playbooks and install collections on every fresh clone or CI job:
---
collections:
- name: community.general
version: "9.5.2"
- name: ansible.posix
version: "2.1.0"ansible-galaxy collection install -r requirements.ymlansible-galaxy role install does not install collection modules—use ansible-galaxy collection install or ansible-galaxy install -r requirements.yml when the file lists both roles and collections. See Galaxy roles and requirements for role-specific installs.
Fix Execution Environment Collection Mismatch
On Ansible Navigator, AWX, or other container-based runners, collections inside the execution environment image matter—not only what is installed on the host shell.
Typical failure: collection installed on your laptop, but the EE image was never rebuilt. Add Ansible collections to the execution environment's Galaxy requirements file, usually requirements.yml, and rebuild the image so ansible-galaxy collection install -r runs during the build. Use bindep.txt for operating-system packages required by those collections or modules, not for installing Ansible collections themselves.
Inside the execution environment, verify the same things you check on the host:
ansible --versionansible-galaxy collection listansible-config dump --only-changedansible-doc -t module community.general.sefcontextIf those commands work on your laptop but fail inside the EE, rebuild the EE with the required collection listed in the EE requirements.yml—the pattern matches CI: install collections where the playbook actually runs.
Fix Undefined Variable Errors
| Cause | Fix |
|---|---|
| Typo in variable name | Compare task spelling with vars files |
| Variable under wrong inventory group | ansible-inventory --graph |
group_vars filename does not match group |
Rename to match group name exactly |
| Role variable missing | Add defaults/main.yml or pass role parameter |
| Registered variable used before task ran | Reorder tasks or handle skipped results |
| Variable defined for one host only | Use when: or define per-host vars |
| Nested dictionary key missing | Debug parent dict before accessing child keys |
Official Ansible variable docs describe where variables can be defined and how precedence selects the final value. This guide focuses on resolution failures, not the full precedence chain—see variable precedence.
Undefined variable at task time:
- name: Use missing variable
ansible.builtin.debug:
msg: "Port is {{ app_port }}"Sample output:
fatal: [rocky2]: FAILED! => {"msg": "The task includes an option with an undefined variable. The error was: 'app_port' is undefined. 'app_port' is undefined\n\nThe error appears to be in '.../undefined-var-demo.yml': line 6, column 7, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n tasks:\n - name: Use missing variable\n ^ here\n"}Fix Undefined Variables in group_vars and host_vars
Inventory group names must match group_vars filenames. For an inventory file at inventory/hosts, place group variables in inventory/group_vars/<group>.yml, not only at the project root unless your layout loads them.
Confirm groups and host names:
cd ~/ansible-projectansible-inventory -i inventory/hosts --graphSample output:
@all:
|--@ungrouped:
|--@lab:
| |--rocky2Inspect merged variables for one host:
ansible-inventory -i inventory/hosts --host rocky2Sample output:
{
"ansible_host": "192.168.56.109",
"ansible_user": "ansible",
"web_port": 443
}| Problem | Example |
|---|---|
| Group file mismatch | Group is web, file is group_vars/webservers.yml |
| Host file mismatch | Inventory name is rocky2, file is host_vars/192.168.56.109.yml |
| Wrong inventory path | Vars exist but -i points elsewhere |
| Bad YAML in vars file | Vars file fails to parse; run ansible-inventory --host <host> or ansible-playbook --syntax-check to expose the error |
For predictable tutorials, keep group_vars/ and host_vars/ beside the inventory source—for example inventory/group_vars/lab.yml. ansible-playbook can also load group_vars/ and host_vars/ from the playbook directory, but ad-hoc commands such as ansible may need --playbook-dir to see playbook-relative vars.
Details on layout and patterns: group_vars and host_vars and inventory files.
Fix Undefined Variables in Roles
Put overridable values in roles/<name>/defaults/main.yml. Required inputs should have defaults or be validated with assert at role start.
# roles/webserver/defaults/main.yml
---
web_package: httpd
web_service: httpd
web_port: 8080Callers override with role parameters or inventory vars. Do not rely on vars/main.yml for values other plays should change—defaults are the safe baseline. See Ansible roles for role layout.
Fix Undefined Variables in Templates
Templates render Jinja2 before writing the file. A missing variable fails the template task:
fatal: [rocky2]: FAILED! => {"changed": false, "msg": "AnsibleUndefinedVariable: 'app_env' is undefined. 'app_env' is undefined"}Print template inputs before the template task:
- name: Show template inputs
ansible.builtin.debug:
msg:
app_port: "{{ app_port | default('undefined') }}"
app_env: "{{ app_env | default('undefined') }}"For template module usage, see template module and Jinja2.
Use Jinja2 default Filter Safely
The default filter returns a fallback when a variable is undefined.
| Use case | Good or bad? | Reason |
|---|---|---|
| Optional banner text | Good | Default is harmless |
| Required database password | Bad | Hides missing secret |
| Optional list | Good with default([]) |
Avoids loop failure on empty list |
| Required app port | Usually bad | Define in inventory or use assert |
Use default() for optional presentation values—not to mask broken inventory or missing secrets.
For optional module parameters, use default(omit) so Ansible does not send the parameter at all:
- name: Create file with optional mode
ansible.builtin.file:
path: /tmp/demo.txt
state: touch
mode: "{{ file_mode | default(omit) }}"For required values, prefer assert in the play. In templates, the mandatory filter fails when a value is undefined instead of substituting a placeholder:
{{ database_password | mandatory }}Debug Variables Before Changing the Playbook
Before rewriting tasks, confirm what Ansible actually loaded:
- name: Debug variable
ansible.builtin.debug:
var: app_config
- name: Validate required variable
ansible.builtin.assert:
that:
- app_port is defined
fail_msg: "app_port must be defined in inventory or role defaults"Use ansible-inventory --host <hostname> to see merged inventory variables. Limit to one host with -l rocky2 while debugging.
When a variable exists but behaves wrongly, check its type—string versus list, or dict versus scalar:
- name: Show variable type
ansible.builtin.debug:
msg:
value: "{{ app_config | default('undefined') }}"
type: "{{ app_config | default('undefined') | type_debug }}"Full debug, assert, and verbosity workflows live in debug Ansible playbooks.
Common Real-World Examples
Fix missing community.general collection
Error: couldn't resolve module/action 'community.general.sefcontext'.
ansible-galaxy collection install community.general
ansible-galaxy collection list community.generalUpdate the play to use community.general.sefcontext and rerun.
Fix couldn't resolve module/action with FQCN
Replace ambiguous short names:
# Risky when collection path differs between environments
sefcontext:
target: '/srv/www/html(/.*)?'
setype: httpd_sys_content_t
state: present
# Explicit
community.general.sefcontext:
target: '/srv/www/html(/.*)?'
setype: httpd_sys_content_t
state: presentFix collection works locally but fails in CI
Add a CI step before ansible-playbook:
ansible-galaxy collection install -r requirements.yml
ansible-galaxy collection list
ansible-playbook playbooks/site.ymlCompare ansible --version collection paths in CI and on your laptop.
Fix undefined variable from wrong inventory group
Symptom: 'web_port' is undefined but you created group_vars/webservers.yml while the play uses hosts: web.
Fix: rename the file to inventory/group_vars/web.yml or change the inventory group to match the filename. Verify with ansible-inventory --host rocky2.
Fix undefined role variable with defaults
Symptom: role task references {{ web_port }} and fails on first run.
Fix: add web_port: 8080 to roles/webserver/defaults/main.yml, or pass web_port when you include the role.
Fix optional template variable with default filter
For an optional footer line in a template:
{% if banner_text is defined and banner_text | length > 0 %}
# Banner: {{ banner_text }}
{% endif %}Or in the template body: {{ banner_text | default('') }}. Do not use default() for required secrets or ports.
Common Mistakes
| Mistake | Why it causes problems |
|---|---|
| Using short module names for collection modules | Ansible may not resolve the module in every environment |
| Installing a collection on your laptop only | CI, EE, or another user cannot find it |
Forgetting to commit requirements.yml |
Fresh clones miss required collections |
| Installing roles instead of collections | ansible-galaxy role install does not install collection modules |
Checking ansible-galaxy collection list as the wrong user |
Runtime user may have a different collection path |
Ignoring ansible --version collection paths |
Playbook may use a different config than expected |
Using default() for required variables |
Hides broken inventory or missing secrets |
| Defining variables under the wrong inventory group | group_vars file never applies |
Using host_vars named after IP when inventory uses an alias |
Host vars file does not match inventory hostname |
| Debugging only the task, not the variable source | The same error returns on the next play |
Best Practices
| Practice | Why |
|---|---|
| Use FQCN for non-built-in modules | Removes ambiguity across machines |
Commit requirements.yml with exact version pins |
Reproducible CI; avoids pulling incompatible latest collections |
Run ansible-galaxy collection install -r in CI |
Collections exist before playbook execution |
Check ansible --version when paths differ |
Confirms config and collection locations |
Match group_vars / host_vars names to inventory |
Variables load predictably |
Put role overridables in defaults/main.yml |
Avoids undefined-variable failures in shared roles |
Use assert for required role inputs |
Fails early with a clear message |
Use default() only for optional values |
Does not hide missing required data |
Run ansible-doc <fqcn> before first use |
Confirms module exists on control node |
| Link to the troubleshooting hub for SSH or inventory layers | Fixes the right problem first |
Summary
Collection and variable errors mean Ansible could not resolve a name: install and verify collections with ansible-galaxy collection list, use FQCN, align COLLECTIONS_PATHS and requirements.yml with CI and execution environments, and fix undefined variables by matching inventory names to group_vars / host_vars, role defaults, and template inputs. For layer-by-layer diagnosis start with the troubleshooting hub; for runtime inspection use debug Ansible playbooks.
References
- Using Ansible collections — install paths and FQCN
- ansible-galaxy collection install — install and list collections
- Ansible variables — where variables are defined
- default filter — Jinja2 undefined fallback
- mandatory filter — fail on undefined required values
- Ansible collections overview — FQCN and RHEL System Roles
- Ansible variables — variable basics
- Debug Ansible playbooks —
debug,assert, and check mode

