Skip to content

ansible-lint cannot find local modules used in roles #4782

@andreymal

Description

@andreymal
Summary

ansible-lint cannot find a local module that works just fine, but only when the module is used in a role

Issue Type
  • Bug Report
OS / ENVIRONMENT

Ubuntu 24.04 (pip), Arch (OS package)

$ ansible-lint --version
ansible-lint 25.9.0 using ansible-core:2.19.2 ansible-compat:25.8.1 ruamel-yaml:0.18.15 ruamel-yaml-clib:0.2.12
STEPS TO REPRODUCE

File structure:

library/
└ mylib.py
roles/
└ myrole/
  └ tasks/
    └ main.yml
playbook.yml

library/mylib.py:

from ansible.module_utils.basic import AnsibleModule

def main():
    module = AnsibleModule(argument_spec={})
    module.exit_json(msg="I am working!")

if __name__ == "__main__":
    main()

roles/myrole/tasks/main.yml:

- name: Run mylib in a role
  mylib: {}

playbook.yml:

---
- name: Test
  hosts: localhost
  tasks:
    - name: Run mylib
      mylib: {}
  roles:
    - role: myrole
Desired Behavior

Since the playbook works without any errors:

$ ansible-playbook -v playbook.yml
No config file found; using defaults
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

PLAY [Test] ***

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

TASK [myrole : Run mylib in a role] ***
ok: [localhost] => {"changed": false, "msg": "I am working!"}

TASK [Run mylib] ***
ok: [localhost] => {"changed": false, "msg": "I am working!"}

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

I expect ansible-lint to also work without false positives

Actual Behavior
$ ansible-lint --project-dir $PWD
WARNING  Listing 1 violation(s) that are fatal
syntax-check[unknown-module]: couldn't resolve module/action 'mylib'. This often indicates a misspelling, missing collection, or incorrect module path.
roles/myrole/tasks/main.yml:1:3


# Rule Violation Summary

  1 syntax-check profile:min tags:core,unskippable

Failed: 1 failure(s), 0 warning(s) in 4 files processed of 4 encountered.
$ ansible-lint -vvvv --project-dir $PWD
DEBUG    Logging initialized to level 10
DEBUG    Options: Options(_skip_ansible_syntax_check=False, cache_dir=PosixPath('/tmp/atest/pb/.ansible'), colored=True, configured=True, cwd=PosixPath('/tmp/atest/pb'), display_relative_path=True, exclude_paths=['.cache', '.git', '.hg', '.svn', '.tox'], format=None, lintables=[], list_rules=False, list_tags=False, write_list=[], write_exclude_list=[], parseable=False, quiet=0, rulesdirs=[PosixPath('/tmp/atest/venv/lib/python3.12/site-packages/ansiblelint/rules')], skip_list=[], tags=[], verbosity=4, warn_list=['experimental', 'jinja[spacing]', 'fqcn[deep]'], mock_filters=[], mock_modules=[], mock_roles=[], loop_var_prefix=None, only_builtins_allow_collections=[], only_builtins_allow_modules=[], var_naming_pattern=None, offline=False, project_dir='/tmp/atest/pb', extra_vars=None, enable_list=[], skip_action_validation=True, strict=False, rules={}, profile=None, task_name_prefix='{stem} | ', sarif_file=None, config_file=None, generate_ignore=False, rulesdir=[], use_default_rules=False, version=False, list_profiles=False, ignore_file=None, yamllint_file=None, max_tasks=100, max_block_depth=20, supported_ansible_also=[])[/]
DEBUG    CWD: /tmp/atest/pb
DEBUG    Logging initialized to level 10
INFO     Collection paths was patched to include extra directories /home/andreymal/.ansible/collections,/usr/share/ansible/collections,/tmp/atest/venv/lib/python3.12/dist-packages,/tmp/atest/venv/lib/python3/dist-packages,/tmp/atest/venv/local/lib/python3.12/dist-packages,/tmp/atest/venv/lib/python3.12/site-packages
DEBUG    Effective yamllint rules used: {'anchors': {'level': 'error', 'forbid-undeclared-aliases': True, 'forbid-duplicated-anchors': False, 'forbid-unused-anchors': False}, 'braces': {'level': 'error', 'forbid': False, 'min-spaces-inside': 0, 'max-spaces-inside': 1, 'min-spaces-inside-empty': -1, 'max-spaces-inside-empty': -1}, 'brackets': {'level': 'error', 'forbid': False, 'min-spaces-inside': 0, 'max-spaces-inside': 0, 'min-spaces-inside-empty': -1, 'max-spaces-inside-empty': -1}, 'colons': {'level': 'error', 'max-spaces-before': 0, 'max-spaces-after': 1}, 'commas': {'level': 'error', 'max-spaces-before': 0, 'min-spaces-after': 1, 'max-spaces-after': 1}, 'comments': {'level': 'warning', 'require-starting-space': True, 'ignore-shebangs': True, 'min-spaces-from-content': 1}, 'comments-indentation': False, 'document-end': False, 'document-start': False, 'empty-lines': {'level': 'error', 'max': 2, 'max-start': 0, 'max-end': 0}, 'empty-values': False, 'float-values': False, 'hyphens': {'level': 'error', 'max-spaces-after': 1}, 'indentation': {'level': 'error', 'spaces': 'consistent', 'indent-sequences': True, 'check-multi-line-strings': False}, 'key-duplicates': {'level': 'error', 'forbid-duplicated-merge-keys': False}, 'key-ordering': False, 'line-length': {'level': 'error', 'max': 160, 'allow-non-breakable-words': True, 'allow-non-breakable-inline-mappings': False}, 'new-line-at-end-of-file': {'level': 'error'}, 'new-lines': {'level': 'error', 'type': 'unix'}, 'octal-values': {'forbid-implicit-octal': True, 'forbid-explicit-octal': True, 'level': 'error'}, 'quoted-strings': False, 'trailing-spaces': {'level': 'error'}, 'truthy': {'level': 'warning', 'allowed-values': ['true', 'false'], 'check-keys': True}}
INFO     Set ANSIBLE_LIBRARY=/tmp/atest/pb/.ansible/modules:/home/andreymal/.ansible/plugins/modules:/usr/share/ansible/plugins/modules
INFO     Set ANSIBLE_COLLECTIONS_PATH=/tmp/atest/pb/.ansible/collections:/home/andreymal/.ansible/collections:/usr/share/ansible/collections:/tmp/atest/venv/lib/python3.12/dist-packages:/tmp/atest/venv/lib/python3/dist-packages:/tmp/atest/venv/local/lib/python3.12/dist-packages:/tmp/atest/venv/lib/python3.12/site-packages
INFO     Set ANSIBLE_ROLES_PATH=/tmp/atest/pb/.ansible/roles:roles:/home/andreymal/.ansible/roles:/usr/share/ansible/roles:/etc/ansible/roles
DEBUG    Effective yamllint rules used: {'anchors': {'level': 'error', 'forbid-undeclared-aliases': True, 'forbid-duplicated-anchors': False, 'forbid-unused-anchors': False}, 'braces': {'level': 'error', 'forbid': False, 'min-spaces-inside': 0, 'max-spaces-inside': 1, 'min-spaces-inside-empty': -1, 'max-spaces-inside-empty': -1}, 'brackets': {'level': 'error', 'forbid': False, 'min-spaces-inside': 0, 'max-spaces-inside': 0, 'min-spaces-inside-empty': -1, 'max-spaces-inside-empty': -1}, 'colons': {'level': 'error', 'max-spaces-before': 0, 'max-spaces-after': 1}, 'commas': {'level': 'error', 'max-spaces-before': 0, 'min-spaces-after': 1, 'max-spaces-after': 1}, 'comments': {'level': 'warning', 'require-starting-space': True, 'ignore-shebangs': True, 'min-spaces-from-content': 1}, 'comments-indentation': False, 'document-end': False, 'document-start': False, 'empty-lines': {'level': 'error', 'max': 2, 'max-start': 0, 'max-end': 0}, 'empty-values': False, 'float-values': False, 'hyphens': {'level': 'error', 'max-spaces-after': 1}, 'indentation': {'level': 'error', 'spaces': 'consistent', 'indent-sequences': True, 'check-multi-line-strings': False}, 'key-duplicates': {'level': 'error', 'forbid-duplicated-merge-keys': False}, 'key-ordering': False, 'line-length': {'level': 'error', 'max': 160, 'allow-non-breakable-words': True, 'allow-non-breakable-inline-mappings': False}, 'new-line-at-end-of-file': {'level': 'error'}, 'new-lines': {'level': 'error', 'type': 'unix'}, 'octal-values': {'forbid-implicit-octal': True, 'forbid-explicit-octal': True, 'level': 'error'}, 'quoted-strings': False, 'trailing-spaces': {'level': 'error'}, 'truthy': {'level': 'warning', 'allowed-values': ['true', 'false'], 'check-keys': True}}
DEBUG    Excluded: .ansible
DEBUG    Excluded: library/__pycache__
DEBUG    Added role: roles/myrole (role)
DEBUG    Excluded: .ansible
DEBUG    Excluded: library/__pycache__
DEBUG    data set to None for library/mylib.py due to being 'python' (text/python) kind.
INFO     Executing syntax check on playbook playbook.yml (0.66s)
INFO     Executing syntax check on role roles/myrole (0.66s)
DEBUG    Examining playbook.yml of type playbook
DEBUG    Running rule internal-error
DEBUG    Running rule load-failure
DEBUG    Running rule parser-error
DEBUG    Running rule warning
DEBUG    Running rule yaml
DEBUG    Running rule args
DEBUG    Running rule avoid-implicit
DEBUG    Running rule command-instead-of-module
DEBUG    Running rule command-instead-of-shell
DEBUG    Running rule complexity
DEBUG    Running rule deprecated-bare-vars
DEBUG    Running rule deprecated-local-action
DEBUG    Running rule deprecated-module
DEBUG    Running rule fqcn
DEBUG    Running rule galaxy
DEBUG    Running rule ignore-errors
DEBUG    Running rule inline-env-var
DEBUG    Running rule jinja
DEBUG    Running rule key-order
DEBUG    Running rule latest
DEBUG    Running rule literal-compare
DEBUG    Running rule loop-var-prefix
DEBUG    Running rule meta-incorrect
DEBUG    Running rule meta-no-tags
DEBUG    Running rule meta-runtime
DEBUG    Running rule meta-video-links
DEBUG    Running rule name
DEBUG    Running rule no-changed-when
DEBUG    Running rule no-free-form
DEBUG    Running rule no-handler
DEBUG    Running rule no-jinja-when
DEBUG    Running rule no-relative-paths
DEBUG    Running rule no-tabs
DEBUG    Running rule package-latest
DEBUG    Running rule partial-become
DEBUG    Running rule playbook-extension
DEBUG    Running rule risky-file-permissions
DEBUG    Running rule risky-octal
DEBUG    Running rule risky-shell-pipe
DEBUG    Running rule role-name
DEBUG    Running rule run-once
DEBUG    Running rule sanity
DEBUG    Running rule schema
DEBUG    Running rule var-naming
DEBUG    Examining roles/myrole of type role
DEBUG    Running rule internal-error
DEBUG    Running rule load-failure
DEBUG    Running rule parser-error
DEBUG    Running rule warning
DEBUG    Running rule yaml
DEBUG    Running rule args
DEBUG    Running rule avoid-implicit
DEBUG    Running rule command-instead-of-module
DEBUG    Running rule command-instead-of-shell
DEBUG    Running rule complexity
DEBUG    Running rule deprecated-bare-vars
DEBUG    Running rule deprecated-local-action
DEBUG    Running rule deprecated-module
DEBUG    Running rule fqcn
DEBUG    Running rule galaxy
DEBUG    Running rule ignore-errors
DEBUG    Running rule inline-env-var
DEBUG    Running rule jinja
DEBUG    Running rule key-order
DEBUG    Running rule latest
DEBUG    Running rule literal-compare
DEBUG    Running rule loop-var-prefix
DEBUG    Running rule meta-incorrect
DEBUG    Running rule meta-no-tags
DEBUG    Running rule meta-runtime
DEBUG    Running rule meta-video-links
DEBUG    Running rule name
DEBUG    Running rule no-changed-when
DEBUG    Running rule no-free-form
DEBUG    Running rule no-handler
DEBUG    Running rule no-jinja-when
DEBUG    Running rule no-relative-paths
DEBUG    Running rule no-tabs
DEBUG    Running rule package-latest
DEBUG    Running rule partial-become
DEBUG    Running rule playbook-extension
DEBUG    Running rule risky-file-permissions
DEBUG    Running rule risky-octal
DEBUG    Running rule risky-shell-pipe
DEBUG    Running rule role-name
DEBUG    Running rule run-once
DEBUG    Running rule sanity
DEBUG    Running rule schema
DEBUG    Running rule var-naming
DEBUG    Examining library/mylib.py of type python
DEBUG    Running rule internal-error
DEBUG    Running rule load-failure
DEBUG    Running rule parser-error
DEBUG    Running rule warning
DEBUG    Running rule yaml
DEBUG    Running rule args
DEBUG    Running rule avoid-implicit
DEBUG    Running rule command-instead-of-module
DEBUG    Running rule command-instead-of-shell
DEBUG    Running rule complexity
DEBUG    Running rule deprecated-bare-vars
DEBUG    Running rule deprecated-local-action
DEBUG    Running rule deprecated-module
DEBUG    Running rule fqcn
DEBUG    Running rule galaxy
DEBUG    Running rule ignore-errors
DEBUG    Running rule inline-env-var
DEBUG    Running rule jinja
DEBUG    Running rule key-order
DEBUG    Running rule latest
DEBUG    Running rule literal-compare
DEBUG    Running rule loop-var-prefix
DEBUG    Running rule meta-incorrect
DEBUG    Running rule meta-no-tags
DEBUG    Running rule meta-runtime
DEBUG    Running rule meta-video-links
DEBUG    Running rule name
DEBUG    Running rule no-changed-when
DEBUG    Running rule no-free-form
DEBUG    Running rule no-handler
DEBUG    Running rule no-jinja-when
DEBUG    Running rule no-relative-paths
DEBUG    Running rule no-tabs
DEBUG    Running rule package-latest
DEBUG    Running rule partial-become
DEBUG    Running rule playbook-extension
DEBUG    Running rule risky-file-permissions
DEBUG    Running rule risky-octal
DEBUG    Running rule risky-shell-pipe
DEBUG    Running rule role-name
DEBUG    Running rule run-once
DEBUG    Running rule sanity
DEBUG    Running rule schema
DEBUG    Running rule var-naming
WARNING  Listing 1 violation(s) that are fatal
syntax-check[unknown-module]: couldn't resolve module/action 'mylib'. This often indicates a misspelling, missing collection, or incorrect module path.
roles/myrole/tasks/main.yml:1:3

DEBUG    Attempting to release lock 139644241116528 on /tmp/atest/pb/.ansible/.lock
DEBUG    Lock 139644241116528 released on /tmp/atest/pb/.ansible/.lock

DEBUG    Determined rule-profile order: {'internal-error': (0, 'min'), 'load-failure': (1, 'min'), 'parser-error': (2, 'min'), 'syntax-check': (3, 'min'), 'command-instead-of-module': (4, 'basic'), 'command-instead-of-shell': (5, 'basic'), 'deprecated-bare-vars': (6, 'basic'), 'deprecated-local-action': (7, 'basic'), 'deprecated-module': (8, 'basic'), 'inline-env-var': (9, 'basic'), 'key-order': (10, 'basic'), 'literal-compare': (11, 'basic'), 'jinja': (12, 'basic'), 'no-free-form': (13, 'basic'), 'no-jinja-when': (14, 'basic'), 'no-tabs': (15, 'basic'), 'partial-become': (16, 'basic'), 'playbook-extension': (17, 'basic'), 'role-name': (18, 'basic'), 'schema': (19, 'basic'), 'name': (20, 'basic'), 'var-naming': (21, 'basic'), 'yaml': (22, 'basic'), 'name[template]': (23, 'moderate'), 'name[imperative]': (24, 'moderate'), 'name[casing]': (25, 'moderate'), 'spell-var-name': (26, 'moderate'), 'avoid-implicit': (27, 'safety'), 'latest': (28, 'safety'), 'package-latest': (29, 'safety'), 'risky-file-permissions': (30, 'safety'), 'risky-octal': (31, 'safety'), 'risky-shell-pipe': (32, 'safety'), 'galaxy': (33, 'shared'), 'ignore-errors': (34, 'shared'), 'layout': (35, 'shared'), 'meta-incorrect': (36, 'shared'), 'meta-no-tags': (37, 'shared'), 'meta-video-links': (38, 'shared'), 'meta-version': (39, 'shared'), 'meta-runtime': (40, 'shared'), 'no-changed-when': (41, 'shared'), 'no-changelog': (42, 'shared'), 'no-handler': (43, 'shared'), 'no-relative-paths': (44, 'shared'), 'max-block-depth': (45, 'shared'), 'max-tasks': (46, 'shared'), 'unsafe-loop': (47, 'shared'), 'avoid-dot-notation': (48, 'production'), 'sanity': (49, 'production'), 'fqcn': (50, 'production'), 'import-task-no-when': (51, 'production'), 'meta-no-dependencies': (52, 'production'), 'single-entry-point': (53, 'production'), 'use-loop': (54, 'production')}[/]
# Rule Violation Summary

  1 syntax-check profile:min tags:core,unskippable

DEBUG    Found virtualenv, assuming `pip3 install` will work.
DEBUG    Registered VCS backend: bzr
DEBUG    Registered VCS backend: git
DEBUG    Registered VCS backend: hg
DEBUG    Registered VCS backend: svn
DEBUG    Found ansible-lint 25.9.0 dist
Failed: 1 failure(s), 0 warning(s) in 4 files processed of 4 encountered.

Metadata

Metadata

Assignees

No one assigned

    Labels

    newTriage required

    Type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions