ansible/ansible · error · AnsibleParserError
The value of the task `args` keyword is invalid.
Error message
The value of the task `args` keyword is invalid.
What it means
ModuleArgsParser validates the task-level `args` keyword: it must be a mapping, a string that is possibly an all-Jinja template (deferred), or None (deprecated-empty). Any other type — list, int, bool — raises AnsibleParserError 'The value of the task `args` keyword is invalid' with help text requiring a mapping or template resolving to one.
Source
Thrown at lib/ansible/parsing/mod_args.py:174
# final args are the ones we'll eventually return, so first update
# them with any additional args specified, which have lower priority
# than those which may be parsed/normalized next
final_args = dict()
if additional_args is not Sentinel:
if isinstance(additional_args, str) and _jinja_bits.is_possibly_all_template(additional_args):
final_args['_variable_params'] = additional_args
elif isinstance(additional_args, dict):
final_args.update(additional_args)
elif additional_args is None:
Display().deprecated(
msg="Ignoring empty task `args` keyword.",
version="2.23",
help_text='A mapping or template which resolves to a mapping is required.',
obj=self._task_ds,
)
else:
raise AnsibleParserError(
message='The value of the task `args` keyword is invalid.',
help_text='A mapping or template which resolves to a mapping is required.',
obj=additional_args,
)
# how we normalize depends if we figured out what the module name is
# yet. If we have already figured it out, it's a 'new style' invocation.
# otherwise, it's not
if action is not None:
args = self._normalize_new_style_args(thing, action, additional_args)
else:
(action, args) = self._normalize_old_style_args(thing)
# this can occasionally happen, simplify
if args and 'args' in args:
tmp_args = args.pop('args')
if isinstance(tmp_args, str):View on GitHub (pinned to 9cf16a4aca)
Solutions
- Make args a proper YAML mapping: args: {src: x, dest: y}
- If args must be dynamic, use a single Jinja expression that resolves to a dict: args: "{{ item.args }}"
- Remove the args keyword and put parameters directly under the module name
Example fix
# before
- ansible.builtin.copy:
args:
- src
- dest
# after
- ansible.builtin.copy:
args:
src: /tmp/a
dest: /tmp/b Defensive patterns
Strategy: validation
Validate before calling
# Pre-flight check for dynamically built tasks
def task_args_ok(task: dict) -> bool:
a = task.get('args', '__missing__')
return a == '__missing__' or isinstance(a, (str, dict)) or a is None
if not task_args_ok(task):
raise AnsibleError(f"task 'args' must be a mapping or template, got {type(task['args']).__name__}") Type guard
def is_valid_args(value) -> bool:
"""task `args` must be a Mapping, all-Jinja string, or None."""
if value is None or isinstance(value, dict):
return True
if isinstance(value, str) and '{{' in value and '}}' in value:
return True
return False Prevention
- Write args as an indented YAML mapping, never a list or scalar
- ansible-playbook --syntax-check catches this before any host is contacted
- When templating whole task dicts, lint the result with a schema checker
When it happens
Trigger: A task like `- copy: ...` plus `args: [src, dest]` (list), or `args: 42`; a Jinja expression resolving to a non-mapping string that is not recognized as fully templated.
Common situations: YAML indentation errors turning the args mapping into a string/scalar; copy-pasting k=v strings into args; migrating old shorthand args into the args keyword incorrectly.
Related errors
- Invalid parameter specified beginning with keyword '_ansible
- action and local_action are mutually exclusive
- conflicting action statements: %s, %s
- no module/action detected in task.
- unexpected parameter type in action: %s
AI-assisted analysis of ansible/ansible@9cf16a4aca (2026-08-15).
Data as JSON: /api/errors/34b5f273024daa5f.
Report an issue: GitHub.