Fix Ansible Collection Not Found, Module Not Found and Undefined Variable Errors

Fix Ansible collection not found, module not found, FQCN resolution, collection path mismatches, and undefined variable errors in inventory, roles, and templates—with commands and real error output.

Published

Updated

Read time 15 min read

Reviewed byDeepak Prasad

Fix Ansible collection not found, module not found and undefined variable errors on Rocky Linux 10

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.

NOTE
This chapter is part of the GoLinuxCloud Ansible tutorial (RHCE EX294). Follow along from ~/ansible-project, inventory group lab, and playbooks in playbooks/. Use your own host names and paths if yours differ.
IMPORTANT
This article fixes name resolution—collections, modules, FQCN, paths, and undefined variables. It does not replace the collections overview for install workflows, modules and ansible-doc for option lookup, variable precedence for full precedence chains, or the debug guide for check mode and verbosity.


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:

bash
ansible-galaxy collection list

Sample output:

output
# /usr/share/ansible/collections/ansible_collections
Collection    Version
------------- -------
ansible.posix 2.1.0

Confirm the module FQCN exists and read its options (-t module limits ansible-doc to modules):

bash
ansible-doc -t module ansible.builtin.copy

If 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:

yaml
- name: Manage SELinux file context
  nonexistent.collection.sefcontext:
    target: '/srv/www/html(/.*)?'
    setype: httpd_sys_content_t
    state: present

Sample output:

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:

yaml
- name: Manage SELinux file context
  community.general.sefcontext:
    target: '/srv/www/html(/.*)?'
    setype: httpd_sys_content_t
    state: present

Confirm the module exists on the control node:

bash
cd ~/ansible-project
bash
ansible-doc community.general.sefcontext

Sample output:

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:

bash
ansible-galaxy collection install community.general

List installed collections and versions:

bash
ansible-galaxy collection list

Sample output:

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.5

Filter to one namespace:

bash
ansible-galaxy collection list community.general

Pin 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:

yaml
---
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.

bash
ansible-galaxy collection install -r requirements.yml

Sample output:

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:

bash
ansible-doc nonexistent.module

Sample output:

output
[WARNING]: nonexistent.module was not found

During a play, Ansible may report:

output
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:

bash
ansible-doc -t module community.general.sefcontext
bash
ansible-doc -t lookup ansible.builtin.file

Sample output:

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 Ansible

For Jinja2 filters, use -t filter:

bash
ansible-doc -t filter ansible.builtin.default
bash
ansible-doc -t filter ansible.builtin.mandatory
bash
ansible-doc -t filter ansible.builtin.type_debug

Use 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:

bash
cd ~/ansible-project
bash
ansible --version

Sample output:

output
ansible [core 2.16.16]
  config file = /home/ansible/ansible-project/ansible.cfg
  ansible collection location = /home/ansible/.ansible/collections:/usr/share/ansible/collections

Show active config overrides:

bash
ansible-config dump --only-changed

Sample output:

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:

bash
ansible-config dump | grep -i COLLECTIONS_PATH

Sample output:

output
COLLECTIONS_PATHS(/home/ansible/ansible-project/ansible.cfg) = ['/home/ansible/.ansible/collections', '/usr/share/ansible/collections']
GALAXY_COLLECTIONS_PATH_WARNING(default) = True

The 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:

bash
whoami
bash
which ansible
bash
ansible --version
bash
ansible-galaxy collection list community.general

Run 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:

yaml
---
collections:
  - name: community.general
    version: "9.5.2"
  - name: ansible.posix
    version: "2.1.0"
bash
ansible-galaxy collection install -r requirements.yml

ansible-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:

bash
ansible --version
bash
ansible-galaxy collection list
bash
ansible-config dump --only-changed
bash
ansible-doc -t module community.general.sefcontext

If 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:

yaml
- name: Use missing variable
  ansible.builtin.debug:
    msg: "Port is {{ app_port }}"

Sample output:

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:

bash
cd ~/ansible-project
bash
ansible-inventory -i inventory/hosts --graph

Sample output:

output
@all:
  |--@ungrouped:
  |--@lab:
  |  |--rocky2

Inspect merged variables for one host:

bash
ansible-inventory -i inventory/hosts --host rocky2

Sample output:

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.

yaml
# roles/webserver/defaults/main.yml
---
web_package: httpd
web_service: httpd
web_port: 8080

Callers 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:

output
fatal: [rocky2]: FAILED! => {"changed": false, "msg": "AnsibleUndefinedVariable: 'app_env' is undefined. 'app_env' is undefined"}

Print template inputs before the template task:

yaml
- 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:

yaml
- 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:

jinja2
{{ database_password | mandatory }}

Debug Variables Before Changing the Playbook

Before rewriting tasks, confirm what Ansible actually loaded:

yaml
- 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:

yaml
- 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'.

bash
ansible-galaxy collection install community.general
ansible-galaxy collection list community.general

Update the play to use community.general.sefcontext and rerun.

Fix couldn't resolve module/action with FQCN

Replace ambiguous short names:

yaml
# 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: present

Fix collection works locally but fails in CI

Add a CI step before ansible-playbook:

bash
ansible-galaxy collection install -r requirements.yml
ansible-galaxy collection list
ansible-playbook playbooks/site.yml

Compare 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:

jinja2
{% 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

Frequently Asked Questions

1. What causes couldn't resolve module/action in Ansible?

Usually a typo, missing collection, wrong FQCN, or a collection installed in a different path or user than the playbook runtime. Install the collection and use the fully qualified collection name.

2. How do I check which Ansible collections are installed?

Run ansible-galaxy collection list or ansible-galaxy collection list namespace.collection. Compare paths with ansible --version and COLLECTIONS_PATHS from ansible-config dump.

3. Why is my Ansible variable undefined when group_vars exists?

The group_vars filename or directory must match the inventory group name. For predictable layouts, keep it next to the inventory source, such as inventory/group_vars/lab.yml. ansible-playbook can also load playbook-directory group_vars, while ad-hoc commands may need --playbook-dir.

4. Should I use default() for missing Ansible variables?

Use default() only for optional values. Required variables such as ports, secrets, or app names should be defined in inventory or role defaults, or validated with assert.

5. What is the difference between a collection error and an undefined variable error?

Collection errors mean Ansible cannot find a module or plugin at runtime. Undefined variable errors mean the name was never defined or is not visible in the current host and task scope.
Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …