AnsiblePilot — Master Ansible Automation

AnsiblePilot is the leading resource for learning Ansible automation, DevOps, and infrastructure as code. Browse over 1,400 tutorials covering Ansible modules, playbooks, roles, collections, and real-world examples. Whether you are a beginner or an experienced engineer, our step-by-step guides help you automate Linux, Windows, cloud, containers, and network infrastructure.

Popular Topics

About Luca Berton

Luca Berton is an Ansible automation expert, author of 8 Ansible books published by Apress and Leanpub including "Ansible for VMware by Examples" and "Ansible for Kubernetes by Example", and creator of the Ansible Pilot YouTube channel. He shares practical automation knowledge through tutorials, books, and video courses to help IT professionals and DevOps engineers master infrastructure automation.

Ansible Conflicting Action Statements Error: Causes and Fixes

By Luca Berton · Published 2024-01-01 · Category: installation

Fix the Ansible 'conflicting action statements' error with real examples. Covers duplicate module names, free-form vs YAML syntax mixing, action/local_action.

"Conflicting action statements" means Ansible found two or more module calls in the same task. A task can only have one action — this error tells you something is structurally wrong.

The Error

ERROR! conflicting action statements: ansible.builtin.copy, ansible.builtin.template

The error appears to be in '/path/to/playbook.yml': line 5, column 7

See also: Ansible Troubleshooting Installation Issues on macOS and Python

Cause 1: Two Modules in One Task

The most common cause — accidentally putting two modules in the same task:

# ❌ TWO MODULES in one task
- name: Deploy config
  ansible.builtin.copy:
    src: config.conf
    dest: /etc/myapp/
  ansible.builtin.template:
    src: config.j2
    dest: /etc/myapp/config.conf

# ✅ FIX: Split into two tasks - name: Copy static file ansible.builtin.copy: src: config.conf dest: /etc/myapp/

- name: Deploy template ansible.builtin.template: src: config.j2 dest: /etc/myapp/config.conf

Cause 2: Free-Form and YAML Syntax Mixed

# ❌ free-form (key: value on same line) + YAML args
- name: Install package
  ansible.builtin.apt: name=nginx
    state: present    # This is indented under apt, making it a second statement

# ✅ FIX: Use one syntax - name: Install package ansible.builtin.apt: name: nginx state: present

# Or free-form (not recommended): - name: Install package ansible.builtin.apt: name=nginx state=present

See also: Ansible Permission Denied on Remote Temp Path: Fix Every Cause

Cause 3: action + Module Name

# ❌ Both action: and module name
- name: Copy file
  action: ansible.builtin.copy
  ansible.builtin.copy:
    src: file.txt
    dest: /tmp/

# ✅ FIX: Use one or the other - name: Copy file ansible.builtin.copy: src: file.txt dest: /tmp/

Cause 4: local_action + Module

# ❌ Both local_action and module
- name: Check URL
  local_action: ansible.builtin.uri url=http://localhost
  ansible.builtin.uri:
    url: http://localhost

# ✅ FIX: Use delegate_to instead - name: Check URL ansible.builtin.uri: url: http://localhost delegate_to: localhost

See also: Ansible Template Error While Templating String: Fix Every Jinja2 Error

Cause 5: YAML Indentation Error

# ❌ Bad indentation makes Ansible parse two modules
- name: Deploy app
  ansible.builtin.copy:
    src: app.tar.gz
    dest: /opt/app/
  ansible.builtin.unarchive:    # Same indentation as copy = same task
    src: /opt/app/app.tar.gz
    dest: /opt/app/

# ✅ FIX: Make it two tasks (- at start) - name: Upload archive ansible.builtin.copy: src: app.tar.gz dest: /opt/app/

- name: Extract archive ansible.builtin.unarchive: src: /opt/app/app.tar.gz dest: /opt/app/ remote_src: true

Cause 6: Role Parameters That Look Like Modules

# ❌ Incorrect role include syntax
- name: Run role
  include_role:
    name: nginx
  ansible.builtin.debug:    # This looks like a second action
    msg: "Done"

# ✅ FIX: Separate tasks - name: Run role ansible.builtin.include_role: name: nginx

- name: Report done ansible.builtin.debug: msg: "Done"

Cause 7: Copy-Paste Errors

# ❌ Forgot to add the dash for new task
- name: Install nginx
  ansible.builtin.apt:
    name: nginx
    state: present
  name: Start nginx          # No dash = still same task
  ansible.builtin.systemd:
    name: nginx
    state: started

# ✅ FIX: Add the dash - name: Install nginx ansible.builtin.apt: name: nginx state: present

- name: Start nginx ansible.builtin.systemd: name: nginx state: started

Prevention

Use ansible-lint

ansible-lint playbook.yml
# Catches conflicting actions before you run the playbook

Use YAML Syntax Only

# ✅ Always use YAML syntax (not free-form)
- ansible.builtin.apt:
    name: nginx
    state: present

# ❌ Avoid free-form (harder to read, easier to break) - ansible.builtin.apt: name=nginx state=present

Use an Editor with YAML Validation

VS Code with the Ansible extension highlights structural issues in real-time.

FAQ

Why does Ansible allow only one module per task?

Each task is a single unit of work — it runs one module on one or more hosts. Multiple modules in one task would create ambiguity about execution order, error handling, and change tracking.

How do I run multiple commands in one task?

Use the shell module with a multiline script, or better, create separate tasks for each action. For complex orchestration, use block: to group related tasks.

Does ansible-lint catch this error?

Yes. ansible-lint catches conflicting action statements and many other structural issues before you run the playbook. Run ansible-lint as part of your CI/CD pipeline.

Conclusion

"Conflicting action statements" always means one thing: two modules in one task. The fix is always the same: split them into separate tasks. Use ansible-lint and YAML validation in your editor to catch these before runtime.

Related Articles

Ansible Playbook Structure GuideAnsible Lint: Analyze & Fix PlaybooksAnsible Template Error: Fix Jinja2 ErrorsYAML Multiline Strings in Ansible

Category: installation

Browse all Ansible tutorials · AnsiblePilot Home