Fixing the Ansible 'dict object has no attribute rc' Error in Skipped Tasks

intermediate🔧 Ansible2026-05-01| Ansible 2.9+, any Linux distribution (Ubuntu, RHEL, Debian, CentOS)

Error Message

The conditional check 'result.rc == 0' failed. The error was: 'dict object' has no attribute 'rc'
#register#when#conditional#skip#variable

The Logic Gap: Why Skipped Tasks Break Playbooks

Imagine you're halfway through a production deployment when your playbook stops abruptly. This isn't caused by a server crash or a network glitch. Instead, it's a logical edge case. You have a task that saves its result to a variable, but because that task was skipped, the next step fails while trying to read data that doesn't exist.

This error typically surfaces when you combine a register statement with a when conditional. If the condition is false, the task is bypassed. Consequently, the registered variable lacks standard keys like rc (Return Code), stdout, or stderr.

The Problem Scenario

- name: Check if custom service is running
  command: systemctl is-active my-service
  register: service_check
  when: check_service_health | default(false)
  ignore_errors: yes

- name: Restart service if it failed the check
  service:
    name: my-service
    state: restarted
  when: service_check.rc != 0

If check_service_health is false, the first task is skipped. Ansible still initializes the service_check variable to keep track of the state, but it only contains basic metadata:

{
    "skipped": true,
    "changed": false
}

When the second task tries to access service_check.rc, it hits a wall. Since there is no rc key in a skipped result, Jinja2 throws a fit:

The conditional check 'service_check.rc != 0' failed. The error was: 'dict object' has no attribute 'rc'

Why Ansible Behaves This Way

Ansible's register keyword is persistent. It creates the variable even if the module never executes. This is intentional; it allows you to check variable.skipped in later tasks. However, the specific return values of a module—like the 0 or 1 return code from a shell command—only exist if the command actually ran.

In the world of Jinja2, accessing a missing dictionary key is a fatal error. Your playbook stops immediately to prevent unpredictable behavior.

Solution 1: The 'default' Filter (Quick Fix)

To resolve this quickly, use the Jinja2 default filter. This provides a fallback value so the comparison has something to work with, even if the task was skipped.

- name: Restart service if it failed the check
  service:
    name: my-service
    state: restarted
  when: (service_check.rc | default(0)) != 0

By using default(0), we assume a "success" state if the check never ran. Since 0 != 0 is false, the restart task is safely bypassed without crashing.

Solution 2: Robust Logic with Skip Checks

Relying on defaults can sometimes mask actual failures. A more professional approach is to explicitly check if the task was skipped before evaluating its result.

Explicit Skip Check

- name: Restart service if it failed the check
  service:
    name: my-service
    state: restarted
  when: 
    - service_check is not skipped
    - service_check.rc != 0

Ansible evaluates when lists sequentially. If service_check is not skipped is false, Ansible stops right there. It won't even try to look for the rc attribute on the second line, effectively dodging the error.

Using the 'is defined' Test

If you want maximum safety, verify that the attribute exists at all:

- name: Restart service if it failed the check
  service:
    name: my-service
    state: restarted
  when: service_check.rc is defined and service_check.rc != 0

Verification: Testing the Fix

Don't wait for a production run to see if your logic holds up. You can simulate the skip easily.

  • Trigger the skip: Run your playbook with a variable override: -e "check_service_health=false".
  • Inspect the JSON: Add a debug task to see exactly what Ansible sees:
- name: Debug service check result
  debug:
    var: service_check
  • Run with Verbosity: Use the -v flag. You should see the task output as skipping rather than fatal. This confirms your conditional logic is now handling the missing attributes gracefully.

Key Takeaways

  • Variable Persistence: Registered variables are always created if the task is reached, regardless of whether it executes.
  • Partial Content: A skipped task's dictionary is nearly empty, usually containing only skipped: true.
  • Safe Referencing: Always use | default() or is not skipped when pulling data from tasks that have their own when conditions.
  • Lazy Evaluation: In a list of conditions, Ansible stops at the first false. Put your existence checks first.

Related Error Notes