Introduction
The "'X' is not a valid attribute for a Play" error is one of the most common Ansible errors, and it's almost always caused by a typo in a YAML key. Ansible validates every top-level key in a play against a fixed list of valid attributes. If you mistype tasks as task, or become as becmoe, Ansible rejects the entire play. This article covers the error, every valid play attribute, and the most common typos that trigger it.
The Error
Problematic Playbook
---
- name: File module demo
hosts: all
vars:
myfile: "~/example.txt"
task: # ← typo: should be 'tasks'
- name: Create an empty file
ansible.builtin.file:
path: "{{ myfile }}"
state: touch
Error Output
ERROR! 'task' is not a valid attribute for a Play
The error appears to be in 'playbook.yml': line 2, column 3, but may
be elsewhere in the file depending on the exact syntax problem.
The offending line appears to be:
---
- name: File module demo
^ here
Fixed Playbook
---
- name: File module demo
hosts: all
vars:
myfile: "~/example.txt"
tasks: # ← correct: 'tasks' with 's'
- name: Create an empty file
ansible.builtin.file:
path: "{{ myfile }}"
state: touch
mode: "0644"
All Valid Play Attributes
Here is the complete list of valid top-level keys in an Ansible play:
Required
| Attribute | Description |
|---|---|
hosts | Target hosts/groups for this play |
Common Attributes
| Attribute | Description |
|---|---|
name | Play description |
tasks | List of tasks to execute |
handlers | List of handlers |
vars | Play-level variables |
vars_files | External variable files |
vars_prompt | Interactive variable prompts |
roles | List of roles to include |
pre_tasks | Tasks before roles |
post_tasks | Tasks after roles |
Execution Control
| Attribute | Description |
|---|---|
become | Enable privilege escalation |
become_user | User to escalate to |
become_method | Escalation method (sudo, su) |
become_flags | Extra flags for become |
gather_facts | Collect system facts |
strategy | Execution strategy (linear, free) |
serial | Batch size for rolling updates |
order | Host execution order |
throttle | Max concurrent hosts |
max_fail_percentage | Failure threshold |
any_errors_fatal | Stop on any host failure |
ignore_errors | Continue despite errors |
ignore_unreachable | Skip unreachable hosts |
Connection
| Attribute | Description |
|---|---|
connection | Connection type (ssh, local, winrm) |
port | Connection port |
timeout | Connection timeout |
environment | Environment variables |
module_defaults | Default module parameters |
Other
| Attribute | Description |
|---|---|
collections | Collection search path |
tags | Tags for selective execution |
no_log | Suppress logging |
debugger | Enable task debugger |
diff | Show file diffs |
check_mode | Enable dry-run mode |
fact_path | Custom facts directory |
force_handlers | Run handlers even on failure |
Troubleshooting workflow
Diagnose your full Ansible error
Open the playground with this Ansible troubleshooting — 'not a valid attribute for a Play' context, paste the complete error, and get a corrected next step.
One anonymous generation is available. Do not paste passwords, private keys, tokens, or customer secrets.
Most Common Typos
| Typo | Correct | Error |
|---|---|---|
task | tasks | Missing 's' |
handler | handlers | Missing 's' |
var | vars | Missing 's' |
role | roles | Missing 's' |
host | hosts | Missing 's' |
pre_task | pre_tasks | Missing 's' |
post_task | post_tasks | Missing 's' |
var_files | vars_files | Missing 's' in vars |
become_users | become_user | Extra 's' |
gather_fact | gather_facts | Missing 's' |
enviroment | environment | Misspelling |
conection | connection | Misspelling |
stratagy | strategy | Misspelling |
Other Causes
1. Wrong Indentation Level
# ❌ Tasks at wrong level
---
- name: Example
hosts: all
tasks: # ← not indented under the play
- name: Test
ansible.builtin.ping:
# ✅ Correct indentation
---
- name: Example
hosts: all
tasks:
- name: Test
ansible.builtin.ping:
2. Task-Level Key in Play
# ❌ 'register' is a task attribute, not a play attribute
---
- name: Example
hosts: all
register: result # ← not valid at play level
tasks:
- ansible.builtin.ping:
# ✅ register belongs on the task
---
- name: Example
hosts: all
tasks:
- ansible.builtin.ping:
register: result
3. Tabs Instead of Spaces
YAML doesn't allow tabs. Some editors insert tabs that look like spaces:
# Check for tabs
cat -A playbook.yml | grep '\^I'
# Fix: replace tabs with spaces
sed -i 's/\t/ /g' playbook.yml
4. Multiple Documents Without Separator
# ❌ Missing --- between plays (looks like one play)
- name: Play 1
hosts: web
tasks:
- ansible.builtin.ping:
- name: Play 2 # ← This is fine actually (list of plays)
hosts: db
tasks:
- ansible.builtin.ping:
5. Ansible Version Mismatch
Some attributes were added in newer versions:
# 'throttle' requires Ansible 2.9+
# 'module_defaults' requires Ansible 2.7+
# 'collections' requires Ansible 2.10+
# Check your version
ansible --version
Debugging Tips
Use --syntax-check
# Validate without running
ansible-playbook --syntax-check playbook.yml
Use yamllint
# Install
pip install yamllint
# Check YAML syntax
yamllint playbook.yml
Use ansible-lint
# ansible-lint catches many issues beyond syntax
ansible-lint playbook.yml
Visual Studio Code
Install the Ansible extension (redhat.ansible) for real-time validation:
// .vscode/settings.json
{
"ansible.validation.enabled": true,
"ansible.validation.lint.enabled": true
}
Related Articles
Conclusion
The "'not a valid attribute for a Play' error is almost always a typo — usually a missing 's' (task → tasks, var → vars, handler → handlers). Use ansible-playbook --syntax-check to catch these before running, and install the VS Code Ansible extension for real-time validation. Refer to the complete attribute table above when unsure which keys are valid at the play level.