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

AttributeDescription
hostsTarget hosts/groups for this play

Common Attributes

AttributeDescription
namePlay description
tasksList of tasks to execute
handlersList of handlers
varsPlay-level variables
vars_filesExternal variable files
vars_promptInteractive variable prompts
rolesList of roles to include
pre_tasksTasks before roles
post_tasksTasks after roles

Execution Control

AttributeDescription
becomeEnable privilege escalation
become_userUser to escalate to
become_methodEscalation method (sudo, su)
become_flagsExtra flags for become
gather_factsCollect system facts
strategyExecution strategy (linear, free)
serialBatch size for rolling updates
orderHost execution order
throttleMax concurrent hosts
max_fail_percentageFailure threshold
any_errors_fatalStop on any host failure
ignore_errorsContinue despite errors
ignore_unreachableSkip unreachable hosts

Connection

AttributeDescription
connectionConnection type (ssh, local, winrm)
portConnection port
timeoutConnection timeout
environmentEnvironment variables
module_defaultsDefault module parameters

Other

AttributeDescription
collectionsCollection search path
tagsTags for selective execution
no_logSuppress logging
debuggerEnable task debugger
diffShow file diffs
check_modeEnable dry-run mode
fact_pathCustom facts directory
force_handlersRun 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.

Diagnose this error

Most Common Typos

TypoCorrectError
tasktasksMissing 's'
handlerhandlersMissing 's'
varvarsMissing 's'
rolerolesMissing 's'
hosthostsMissing 's'
pre_taskpre_tasksMissing 's'
post_taskpost_tasksMissing 's'
var_filesvars_filesMissing 's' in vars
become_usersbecome_userExtra 's'
gather_factgather_factsMissing 's'
enviromentenvironmentMisspelling
conectionconnectionMisspelling
stratagystrategyMisspelling

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
}

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.