Ansible ZeroDivisionError

Ansible ZeroDivisionError

- 4 mins

Fixing Ansible’s ZeroDivisionError When Slicing Docker Image Tags

When automating Docker image management with Ansible, especially syncing images from Docker Hub to a private Harbor registry, it’s common to sync images between the two. I decided to change my approach from a python script to an ansible playbook for ease of use and easier maintenance with future development. However, sometimes a solution that should be straightforward can spiral into cryptic errors — like this classic head-scratcher:

ZeroDivisionError: integer division or modulo by zero

Lemme break down what causes this, how to fix it, and a few best practices for working with JSON and Jinja filters in Ansible.


The Setup

I’m using the Docker Hub API to get a list of tags from dockerhub:

uri:
  url: "https://registry.hub.docker.com/v2/repositories/library/nginx/tags?page_size=100"
  return_content: yes
register: docker_tag_results

Then I parse and store only the latest 5 tags per image:


```yaml
set_fact:
  docker_image_tags: >-
    {{
      docker_image_tags | default({}) |
      combine({
        item.item: (
          (item.content | from_json).results | default([]) |
          map(attribute='name') | select('string') | list
        )[0:5]
      })
    }}

From here it looks clean if you run a debug task and print the item.content. However….


The Problem: Jinja’s slice is NOT Python’s slice

In Python, slice(0, 5) would mean: get items from index 0 to 4.

But in Jinja/Ansible, the slice filter is used for chunking lists, like this:

['a', 'b', 'c', 'd', 'e', 'f'] | slice(3)
# → [['a', 'b', 'c'], ['d', 'e', 'f']]

So when you do slice(0, 5), Jinja tries to split the list into 0 groups of 5, triggering the error:

ZeroDivisionError: integer division or modulo by zero

It’s not a slicing operation — it’s a grouping operation.


A Good Solution: Using [0:5]

Instead of using the slice filter, you can just use Python-style list slicing directly:


```yaml
set_fact:
  docker_image_tags: >-
    {{
      docker_image_tags | default({}) |
      combine({
        item.item: (
          (item.content | from_json).results | default([]) |
          map(attribute='name') | select('string') | list
        )[0:5]
      })
    }}

This gives you what you want the first 5 tag names without triggering any errors.


Future Foundation: Avoiding Future Issues

Here are some best practices I found that should be consider in Ansible/Jinja workflows to help prevent future hiccups like this:

1. Use default([]) often

Always apply default([]) when accessing JSON objects like .results to guard against nulls or missing keys.

2. Use select('string')

Some APIs return null values in tag arrays. Filtering with select('string') ensures you don’t pass None into downstream logic.

3. Validate with a fail task

Don’t just assume the tag list exists. Instead, explicitly throw a failure condition when it doesn’t:

- name: Fail if image tag list is empty
  fail:
    msg: "Image  returned no valid tags."
  when: item.value | length == 0
  loop: ""

In Conclusion

The ZeroDivisionError in Ansible might seem mysterious, but it really comes down to Jinja filters acting differently than expected in Ansible. If you’re using slice() thinking it works like Python — it doesn’t. The fix here is to use [0:5] for slicing tags.

TIP: Always code defensively when dealing with JSON data from external APIs.

Project Link

Security Warning

This is a solution to syncing docker and harbor images but requires saving credentials in plain text. The ansible playbook shared here as an example
Consider using GITHUB SECRETS or ANSIBLE VALUT to encrypt secrets when using this in production.

Resources