# Strange behaviour in Ansible 2.19: String converted to an int

**URL:** <https://forum.ansible.com/t/strange-behaviour-in-ansible-2-19-string-converted-to-an-int/45545>\
**Category:** Get Help\
**Tags:** 219, ansible-core\
**Created:** [March 17, 2026, 7:15am UTC](https://forum.ansible.com/t/strange-behaviour-in-ansible-2-19-string-converted-to-an-int/45545 "2026-03-17T07:15:51Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![ildjarn](https://avatars.discourse-cdn.com/v4/letter/i/8491ac/32.png) [@ildjarn](https://forum.ansible.com/u/ildjarn)\
**Post date:** [March 17, 2026, 7:15am UTC](https://forum.ansible.com/t/strange-behaviour-in-ansible-2-19-string-converted-to-an-int/45545/1 "2026-03-17T07:15:51Z")

</div>

I have the following template:

```auto
---
- name: Test MAC address validity
  hosts: localhost
  gather_facts: False

  tasks:
    - name: Debug info
      debug:
        var: macadres

```

I created a job in AWX that calls this playbook, and have turned on ‘Prompt on launch’ for variables.

Case 1:  
I edit the AWX job template and add `macadres: '11:33:22:55:44:11'` in the variables, run the playbook, click next on the ‘Variables’ prompt, and launch.  
Result:

```auto
ok: [localhost] => {
    "macadres": "11:33:22:55:44:11"
}

```

Case 2:  
I edit the AWX job template and clean the Variables section. I run the template, fill in `macadres: '11:33:22:55:44:11` in the Variables prompt, click next, and launch.  
Result:

```auto
ok: [localhost] => {
    "macadres": 8986232651
}

```

So when running as regular variable, everything works fine, when running as extra variable the string is converted to an int.

Ansible: 2.19.7  
AWX: 24.6.1

I also did the same tests with Ansible 2.18.14 and here everything works fine.

I’m not sure if this is a bug, or expected behaviour. If it’s a bug I’ll submit an issue on Github.

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [March 17, 2026, 4:12pm UTC](https://forum.ansible.com/t/strange-behaviour-in-ansible-2-19-string-converted-to-an-int/45545/2 "2026-03-17T16:12:27Z")

</div>

The issue you are running into is a quoting problem. You can produce the same `8986232651` result with other versions of Ansible. The thing is, when the non-quoted value `11:33:22:55:44:11` is ingested, it’s treated as a set of base60 decisextets (_sexagesimal_s as decimal numbers). In other words, it’s being interpreted as a time designation. Observe:

```
((((11 * 60 + 33) * 60 + 22) * 60 + 55) * 60 + 44) * 60 + 11 = 8986232651
    -- -- -- -- -- --

```

Get your quoting right, and the “problem” will go away.

BTW, [_sexagisimal_ ints](https://yaml.org/type/int.html) were introduced in YAML 1.1.  
The YAML 1.2 [spec](https://yaml.org/spec/1.2.2/) doesn’t mention them directly, but it appears applications (such as Ansible) can implement them (and any other scalar presentation) through TAGS.

---

<div class="post-metadata">

**Author:** ![ildjarn](https://avatars.discourse-cdn.com/v4/letter/i/8491ac/32.png) [@ildjarn](https://forum.ansible.com/u/ildjarn)\
**Post date:** [March 17, 2026, 8:28pm UTC](https://forum.ansible.com/t/strange-behaviour-in-ansible-2-19-string-converted-to-an-int/45545/3 "2026-03-17T20:28:10Z")

</div>

Job 1 variables:

 ![image](https://us1.discourse-cdn.com/ansible/original/2X/1/14371d21aba0142d9fd1184c16cd84439727a402.png)

Job 2 variables:

 ![image](https://us1.discourse-cdn.com/ansible/original/2X/1/1f92a1c13928a209ba457f6275bb9701e9c1fc63.png)

One of these jobs converts macadres to an integer, the other one treats it as a string.

Care to guess which one does what?

The playbook that’s run is the exact one that I posted in the first post: 1 task with `debug: var: macadres` in it, so no quotes there.

> [@](#):
>
> You can produce the same `8986232651` result with other versions of Ansible

No, I can not. the EE with Ansible 2.18 will show the string in both cases.

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [March 17, 2026, 9:03pm UTC](https://forum.ansible.com/t/strange-behaviour-in-ansible-2-19-string-converted-to-an-int/45545/4 "2026-03-17T21:03:56Z")

</div>

> [@ildjarn](#):
>
> No, I can not.

Well, I can. With your playbook, unchanged, from the command line by selectively quoting the --extra-vars value in question. Behold:

```plaintext
utoddl@tango:~/ansible$ ansible-playbook macadres.yml -e '{"macadres": "11:33:22:55:44:11"}'

PLAY [Test MAC address validity] *********************************************************************************

TASK [Debug info] *********************************************************************************
ok: [localhost] => 
  macadres: '11:33:22:55:44:11'

PLAY RECAP *********************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

utoddl@tango:~/ansible$ ansible-playbook macadres.yml -e '{"macadres": 11:33:22:55:44:11}'

PLAY [Test MAC address validity] *********************************************************************************

TASK [Debug info] *********************************************************************************
ok: [localhost] => 
  macadres: 8986232651

PLAY RECAP *********************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0

```

The only difference is the quoting of `11:33:22:55:44:11` in the `--extra-vars`. This is with Ansible core 2.18.12, python version = 3.14.3, libyaml = True.

I’m not denying there’s a difference between 2.18 and 2.19 wrt this behavior, but I/you/we _can_ get the same set of values (integer 8986232651 or string ‘11:33:22:55:44:11’) by playing with the quoting of extra-vars in 2.18.

I don’t have a 2.19 handy at the moment, and it could be complicated by having AWX in the mix. Regardless, 8986232651 is the expected decimal integer representation of the sexagesimal scalar 11:33:22:55:44:11.

---

<div class="post-metadata">

**Author:** ![anon81884396](https://avatars.discourse-cdn.com/v4/letter/a/b782af/32.png) [@anon81884396](https://forum.ansible.com/u/anon81884396)\
**Post date:** [March 17, 2026, 10:22pm UTC](https://forum.ansible.com/t/strange-behaviour-in-ansible-2-19-string-converted-to-an-int/45545/5 "2026-03-17T22:22:23Z")

</div>

The behavior of extra vars behaving differently between 2.18 and 2.19 with AWX in the mix seems similar to an issue I had in 2025. The gist is that I had an AWX job template that would take a datetime input string passed as an extravar; when my execution environment used Ansible 2.18 it took the datetime as a literal string and worked for my purposes. When I switched this same job to use a 2.19 EE, the string would not be preserved literally, but interpreted into a different, incompatible with my use case date time string. The thread was here [AWX extra\_var for datetimes exhibiting different behavior between Ansible 2.18 and 2.19](https://forum.ansible.com/t/awx-extra-var-for-datetimes-exhibiting-different-behavior-between-ansible-2-18-and-2-19/44466)

Sorry if this isn’t terribly useful, but there was a reply that did elaborate a bit on why the behavior might be happening. At any rate, I’m glad I got to learn about sexagesimals.

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [March 17, 2026, 10:54pm UTC](https://forum.ansible.com/t/strange-behaviour-in-ansible-2-19-string-converted-to-an-int/45545/6 "2026-03-17T22:54:51Z")

</div>

I think this is very useful information. Particularly that “…AWX writes out the extra vars file in YAML format for consumption by ansible-playbook”.

So the trick is to find a way to express `11:33:22:55:44:11` in a way that will get written to the _extra-vars_ file such that when it gets read back in it ends up as a string instead of a decimal representation of a sexagesimal.

@ildjarn , maybe you can try

```
macadres: !!STR 11:33:55:44:11

```

in your AWX extra-vars?

If you’re willing to try a hacky but reliable method, write it in a way that’s always a string, like

```
macadres: S11:33:55:44:11 # Note the leading 'S' to force it to be a string

```

and strip off the leading non-sexagesimal character in the consuming playbook. It’s not pretty, but I bet it will work.

---

<div class="post-metadata">

**Author:** ![ildjarn](https://avatars.discourse-cdn.com/v4/letter/i/8491ac/32.png) [@ildjarn](https://forum.ansible.com/u/ildjarn)\
**Post date:** [March 18, 2026, 7:18am UTC](https://forum.ansible.com/t/strange-behaviour-in-ansible-2-19-string-converted-to-an-int/45545/7 "2026-03-18T07:18:48Z")

</div>

The data is provided by an external system.  
Fortunately, most, if not all, MAC addresses seem to have at least one letter in them (e.g. 00:1c:4a:3b:55:ff’, which seems to be enough to not get converted to an int. But changing the source data format is not going to work, I’m afraid.  
I’ll guess I’ll add a check in the module to see if the string passed is an integer, if it is, it should be converted back to a sexagesimal number.

FWIW: The same behaviour is shown with variables that are added through an AWX survey, even if the survey type is set to ‘Text’.

---

<div class="post-metadata">

**Author:** ![ildjarn](https://avatars.discourse-cdn.com/v4/letter/i/8491ac/32.png) [@ildjarn](https://forum.ansible.com/u/ildjarn)\
**Post date:** [March 18, 2026, 8:36am UTC](https://forum.ansible.com/t/strange-behaviour-in-ansible-2-19-string-converted-to-an-int/45545/8 "2026-03-18T08:36:58Z")

</div>

Something interesting:

from [Sexagesimal - Wikipedia](https://en.wikipedia.org/wiki/Sexagesimal)

> [@](#):
>
> #### YAML
> 
> In version 1.1[[15]](https://en.wikipedia.org/wiki/Sexagesimal#cite_note-15) of the [YAML](https://en.wikipedia.org/wiki/YAML) data storage format, sexagesimals are supported for plain scalars, and formally specified both for integers[[16]](https://en.wikipedia.org/wiki/Sexagesimal#cite_note-16) and floating point numbers.[[17]](https://en.wikipedia.org/wiki/Sexagesimal#cite_note-17) This has led to confusion, as e.g. some [MAC addresses](https://en.wikipedia.org/wiki/MAC_address) would be recognised as sexagesimals and loaded as integers, where others were not and loaded as strings. In YAML 1.2 support for sexagesimals was dropped.[[18]](https://en.wikipedia.org/wiki/Sexagesimal#cite_note-18)

YAML 1.2 was released in 2009, but pyyaml doesn’t support it (yet).

---

<div class="post-metadata">

**Author:** ![sivel](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/sivel/32/1198_2.png) [@sivel](https://forum.ansible.com/u/sivel)\
**Post date:** [March 18, 2026, 3:00pm UTC](https://forum.ansible.com/t/strange-behaviour-in-ansible-2-19-string-converted-to-an-int/45545/9 "2026-03-18T15:00:07Z")

</div>

I believe the difference in outcome here is the relationship of a few different moving parts. First I’ll show the outcome, and then try to explain why I think it is happening:

```bash
(ansible-2.18) $ ansible localhost -m debug -a var=macadres -e '{macadres: !unsafe 11:33:22:55:44:11}'
localhost | SUCCESS => {
    "macadres": "11:33:22:55:44:11"
}

(ansible-2.19) $ ansible localhost -m debug -a var=macadres -e '{macadres: !unsafe 11:33:22:55:44:11}'
localhost | SUCCESS => {
    "macadres": 8986232651
}

```

As you can see with YAML fed to ansible-core that looks like the following, the outcome is that the value is treated as a string on 2.18, but on 2.19 it is not.

```yaml
{macadres: !unsafe 11:33:22:55:44:11}

```

Prior to 2.19 `!unsafe` implied the value was a string. This is no longer the case with 2.19 and newer.

Now, if you notice, there is a lack of quotes around the mac address in the YAML.

Now, I don’t have researched evindence, so this is just a hypothesis…

While pyyaml has support for sexagesimals, it appears as though whatever serializer/deserializer may be in use in AWX, potentially in the AWX UI, doesn’t support them, and as such doesn’t believe it needs to quote them. As a result, the value that is being sent as extra vars doesn’t contain the quotes.

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [March 18, 2026, 3:45pm UTC](https://forum.ansible.com/t/strange-behaviour-in-ansible-2-19-string-converted-to-an-int/45545/10 "2026-03-18T15:45:07Z")

</div>

> [@ildjarn](#):
>
> I’ll guess I’ll add a check in the module to see if the string passed is an integer, if it is, it should be converted back to a sexagesimal number.

Here’s a filter plugin that will convert these integers back into their MAC addresses. If you pass it a string (i.e. it’s already a MAC) that string will be returned unchanged. Put it in a `filter_plugins` directory relative to your playbook.

```python
#!/usr/bin/python
# filter_plugins/int2sexagesimal.py

def int2sexagesimal(value, minpairs=6):
    """
    Converts an integer to a sexagesimal string
    dd:dd:dd... where each 'd' is a decimal digit,
    each pair of digits is a zero-padded decimal number
    between 00 and 59.

    A non-integer value is returned unchanged.

    This is useful to recover a MAC address which happens to
    contain all decimal digit pairs below 60 and YAML 1.1 interpreted it as
    a sexagesimal integer instead of a string. For example the following
    two YAML 1.1 lines are equivalent:
        mac: 11:33:22:55:44:11
        mac: 8986232651
    They both assign to 'mac' the integer 8986232651 by this conversion:
        ((((11*60 + 33)*60 + 22)*60 + 55)*60 + 44)*60 + 11 = 8986232651
            -- -- -- -- -- --
    int2sexagesimal(8986232651, 6) will return "11:33:22:55:44:11".

    It can also convert seconds into hours, minutes, and seconds
    for values below 216000 - the number of seconds in 60 hours.
    int2sexagesimal(36125, 3) returns "10:02:05"
        i.e 10 hours, 2 minutes, 5 seconds
    """
    if not isinstance(value, int):
        return value

    if value < 0:
        sign = "-"
        value = abs(value)
    else:
        sign = ""

    acc = []
    while True:
      acc.insert(0, f"{value % 60:02d}")
      value //= 60
      if value == 0:
          break

    while len(acc) < minpairs:
      acc.insert(0, '00')

    return f"{sign}{':'.join(acc)}"

class FilterModule(object):
    ''' Ansible core jinja2 filters '''

    def filters(self):
        return {'int2sexagesimal': int2sexagesimal}

if __name__ == " __main__":
    print(f" 86399: {int2sexagesimal( 86399, 3)}")
    print(f" 3600: {int2sexagesimal( 3600, 1)}")
    print(f" -500: {int2sexagesimal( -500, 1)}")
    print(f" 36125: {int2sexagesimal( 36125, 3)}")
    print(f"8986232651: {int2sexagesimal(8986232651, 3)}")
    print(f" 216000: {int2sexagesimal( 216000, 3)}")
    print(f"'a string': {int2sexagesimal('a string', 8)}")

```

Running it as a stand-alone Python program it produces these results:

```txt
$ filter_plugins/int2sexagesimal.py
     86399: 23:59:59
      3600: 01:00:00
      -500: -08:20
     36125: 10:02:05
8986232651: 11:33:22:55:44:11
    216000: 01:00:00:00
'a string': a string

```

Here’s a playbook that uses it:

```yaml
---
- name: Test MAC address recovery with int2sexagesimal filter
  hosts: localhost
  gather_facts: False
  vars:
    macs:
      - 8986232651 # an int; should have been a MAC address
      - 11:33:22:55:44:11 # sexagesimal! contains 2-digit decimals all <60
      - 11:33:22:55:44:1A # contains 'A'
      - 11:63:22:55:44:11 # contains '6' (>5) in the tens place
      - 11:73:22:55:44:11 # contains '7' (>5) in the tens place
      - 'a string' # contains a string
  tasks:
    - name: Display the values and types
      debug:
        msg: "{{ macs | map('int2sexagesimal')
                 | zip(macs | map('int2sexagesimal') | map('type_debug'))
                 | map('join', ', ') }}"

```

and the results of running that playbook:

```plaintext
$ ansible-playbook macadres.yml

PLAY [Test MAC address recovery with int2sexagesimal filter] ******

TASK [Display the values and types] *******************************
ok: [localhost] => 
  msg:
  - 11:33:22:55:44:11, str
  - 11:33:22:55:44:11, str
  - 11:33:22:55:44:1A, AnsibleUnicode
  - 11:63:22:55:44:11, AnsibleUnicode
  - 11:73:22:55:44:11, AnsibleUnicode
  - a string, AnsibleUnicode

```

I briefly made the returned `str` an `AnsibleUnicode` string but the extra code in the filter wasn’t worth it to me. It wasn’t _hard_, it was just … too “magical” for my taste.

I hope this is useful to you, @ildjarn .

---

<div class="post-metadata">

**Author:** ![utoddl](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/utoddl/32/249_2.png) [@utoddl](https://forum.ansible.com/u/utoddl)\
**Post date:** [March 23, 2026, 2:15am UTC](https://forum.ansible.com/t/strange-behaviour-in-ansible-2-19-string-converted-to-an-int/45545/11 "2026-03-23T02:15:08Z")

</div>

Here’s what I hope is my final cut at a `int2sexagesimal` filter. It occurred to me that, besides “fixing” MAC addresses mangled by YAML 1.1, base 60 grouping is only useful for `<hours>:<minutes>:<seconds>`, or maybe `<degrees>:<arc-minutes>:<arc-seconds>`. For other conversions you need to be able to specify a list of bases. This version allows that. The EXAMPLES section includes converting seconds to `['years', 'days', 'hours', 'minutes', 'seconds']` and teaspoons to `['gallons', 'quarts', 'pints', 'cups', 'tablespoons', 'teaspoons']`. I’m sure the lack of that capability has been slowing down the uptake of Ansible, so there’s that excuse gone. 😺 Provide whatever list of bases fits your data.

Not all solutions have a pressing problem. But if you happen to bump into a problem for which this is a solution, you are welcome to it! Happy Filtering!

```python
#!/usr/bin/python
# filter_plugins/int2sexagesimal.py
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import annotations

DOCUMENTATION = '''
name: int2sexagesimal
author: Todd Lewis (https://forum.ansible.com/u/utoddl/summary)
type: filter
short_description: convert integer to sexagesimal string
version_added: "2.18"
description:
  - |
    This filter plugin converts an integer to the equivalent sexagesimal
    string. Sexagesimals consist of decimal integers joined by colons. All but
    the first such integer must be less than the corresponding base (60 by
    default) and left-padded with zeros to a length equal to the length of
    string representing O(base - 1). The first joined integer may be shorter,
    longer, or that same length, possibly left-padded with zeros if both
    O(minparts) and O(maxparts) are specified and equal, but it may be longer
    and larger than the O(base) if necessary to satisfy the O(maxparts) constraint.

  - |
    Commonly used for converting C(seconds) to C(<hours>:<minutes>:<seconds>),
    C(int2sexagesimal) also can be useful to recover a MAC address which was
    inadvertently interpreted as a sexagesimal integer constant by YAML 1.1.
    For example, V(mac: 11:33:22:55:44:11) is equivalent to V(mac: 8986232651) in YAML 1.1.

  - |
    If the input integer is negative, a C(-) will be prefixed to the resulting
    string.

  - |
    A non-integer input value is returned unchanged. This simplifies filtering
    MAC addresses which may have been mangled by YAML 1.1 as described
    above: V({{ macs | map('int2sexagesimal', minparts=6, maxparts=6\\) }})
    Setting minparts equal to maxparts ensures the first part is zero-padded.

options:
  maxparts:
    description:
      - The maximum number of colon-delimited parts in the resulting string
        representation. For example, the result C(143:14:30) has three parts.
    type: integer
    required: false

  minparts:
    description:
      - The minimum number of colon-delimited parts in the resulting
        string representation.
    type: integer
    required: false

  base:
    description:
      - The value at which a corresponding part's value carries over to the
        next leftward part.

      - Does not apply to the left-most part if O(maxparts) is specified and
        reached. For example, the result C(60:59:59) represents 60 hours, 59
        minutes, 59 seconds with the default O(base) of 60 and O(maxparts=3).
        Without setting O(maxparts) the result would be C(01:00:59:59).

      - O(base) can also be a list of integers. For example O(base=[100, 365, 24, 60, 60])
        with O(maxparts=5) would convert seconds to
        C(<years>:<days>:<hours>:<minutes>:<seconds>) and the left-most
        base value would not be used. But with O(maxparts=6)
        and a large enough input value the result would include a leading
        C(<millenium>:) part.

      - If more parts are generated than specified in the O(base) list, the
        left-most (or only) O(base) value is used repeatedly.
    type: integer or list of integers
    required: false
    default: 60

  _value:
    description:
      - The integer to convert to a sexagesimal string, or any other non-integer.
        A non-integer O(_value) is returned unchanged.
    type: raw
    require: true
'''

EXAMPLES = '''
- name: Restore mangled MAC address YAML 1.1 constant
  ansible.builtin.debug:
    msg: '{{ 8986232651 | int2sexagesimal() }}'
# TASK [Restore mangled MAC address YAML 1.1 constant] ******************************
# ok: [localhost] => 
# msg: '11:33:22:55:44:11'

- name: Display converted integer 3753 with various parameters
  ansible.builtin.debug:
    msg:
     - "# 1 hour, 2 minutes, 33 seconds"
     - "3753 | int2sexagesimal() => {{ '%11s' | format(3753 | int2sexagesimal()) }}"
     - "3753 | int2sexagesimal(minparts=2) => {{ '%11s' | format(3753 | int2sexagesimal(minparts=2)) }}"
     - "3753 | int2sexagesimal(maxparts=2) => {{ '%11s' | format(3753 | int2sexagesimal(maxparts=2)) }}"
     - "3753 | int2sexagesimal(maxparts=2, minparts=2) => {{ '%11s' | format(3753 | int2sexagesimal(maxparts=2, minparts=2)) }}"
     - ""
     - "3753 | int2sexagesimal(minparts=3) => {{ '%11s' | format(3753 | int2sexagesimal(minparts=3)) }}"
     - "3753 | int2sexagesimal(maxparts=3) => {{ '%11s' | format(3753 | int2sexagesimal(maxparts=3)) }}"
     - "3753 | int2sexagesimal(maxparts=3, minparts=3) => {{ '%11s' | format(3753 | int2sexagesimal(maxparts=3, minparts=3)) }}"
     - ""
     - "3753 | int2sexagesimal(minparts=4) => {{ '%11s' | format(3753 | int2sexagesimal(minparts=4)) }}"
     - "3753 | int2sexagesimal(maxparts=4) => {{ '%11s' | format(3753 | int2sexagesimal(maxparts=4)) }}"
     - "3753 | int2sexagesimal(maxparts=4, minparts=4) => {{ '%11s' | format(3753 | int2sexagesimal(maxparts=4, minparts=4)) }}"
# TASK [Display converted integer 3753 with various parameters] *********************
# ok: [localhost] => 
# msg:
# - '# 1 hour, 2 minutes, 33 seconds'
# - 3753 | int2sexagesimal() => 01:02:33
# - 3753 | int2sexagesimal(minparts=2) => 01:02:33
# - 3753 | int2sexagesimal(maxparts=2) => 62:33
# - 3753 | int2sexagesimal(maxparts=2, minparts=2) => 62:33
# - ''
# - 3753 | int2sexagesimal(minparts=3) => 01:02:33
# - 3753 | int2sexagesimal(maxparts=3) => 1:02:33
# - 3753 | int2sexagesimal(maxparts=3, minparts=3) => 01:02:33
# - ''
# - 3753 | int2sexagesimal(minparts=4) => 00:01:02:33
# - 3753 | int2sexagesimal(maxparts=4) => 01:02:33
# - 3753 | int2sexagesimal(maxparts=4, minparts=4) => 00:01:02:33

- name: Convert large number of seconds with different bases
  ansible.builtin.debug:
    msg:
      - "# 10749 years, 66 days, 18 hour, 24 minutes, 11 seconds"
      - "{{ 338986232651 | int2sexagesimal(maxparts=5, base=bases) }}"
  vars:
    bases:
      - 100 # years / century
      - 365 # days / year
      - 24 # hours / day
      - 60 # minutes / hour
      - 60 # seconds / minute
# TASK [Convert large number of seconds with different bases] ***********************
# ok: [localhost] => 
# msg:
# - '# 10749 years, 66 days, 18 hour, 24 minutes, 11 seconds'
# - 10749:066:18:24:11

- name: Same conversion as above, but create a dict to label the parts
  ansible.builtin.debug:
    msg:
      - "{{ dict(['years', 'days', 'hours', 'minutes', 'seconds']
                 | zip(338986232651
                       | int2sexagesimal(maxparts=5, minparts=5, base=[100, 365, 24, 60, 60])
                       | split(':')
                       | map('regex_replace', '^0+([0-9]+)', '\\1')
                      )
                ) }}"
# TASK [Same conversion as above, but create a dict to label the parts] *************
# ok: [localhost] => 
# msg:
# - days: '66'
# hours: '18'
# minutes: '24'
# seconds: '11'
# years: '10749'

- name: Convert 3041 teaspoons to gallons, quarts, pints, cups, tablespoons, teaspoons
  ansible.builtin.debug:
    msg:
      - - 3041 teaspoons as a colon-delimited string
        - "{{ 3041 | int2sexagesimal(maxparts=6, minparts=6, base=[4, 2, 2, 16, 3]) }}"
      - - 3041 teaspoons as a dict
        - "{{ dict(['gallons', 'quarts', 'pints', 'cups', 'tablespoons', 'teaspoons']
                 | zip(3041
                       | int2sexagesimal(maxparts=6, minparts=6, base=[4, 2, 2, 16, 3])
                       | split(':')
                       | map('regex_replace', '^0+([0-9]+)', '\\1')
                      )
                ) }}"
#TASK [Convert 3041 teaspoons to gallons, quarts, pints, cups, tablespoons, teaspoons] ****
#ok: [localhost] => 
# msg:
# - - 3041 teaspoons as a colon-delimited string
# - '3:3:1:1:05:2'
# - - 3041 teaspoons as a dict
# - cups: '1'
# gallons: '3'
# pints: '1'
# quarts: '3'
# tablespoons: '5'
# teaspoons: '2'

'''

from ansible.module_utils.common.text.converters import to_text
from ansible.errors import AnsibleError, AnsibleFilterError, AnsibleFilterTypeError

def _flatten(terms):
    ret = []
    for term in terms:
        if isinstance(term, (list, tuple)):
            ret.extend(term)
        else:
            ret.append(term)
    return ret

def int2sexagesimal(value, **kwargs):
    if not isinstance(value, int):
        return value

    minparts = kwargs.get('minparts', None)
    maxparts = kwargs.get('maxparts', None)
    base = kwargs.get('base', 60)

    if minparts and (not isinstance(minparts, int) or minparts < 2):
        raise AnsibleFilterError(f'if specified, minparts must be an int > 1')
    if maxparts and (not isinstance(maxparts, int) or maxparts < 2):
        raise AnsibleFilterError(f'if specified, maxparts must be an int > 1')
    if minparts and maxparts and (minparts > maxparts):
        raise AnsibleFilterError(f'if both are specified, minparts must be less than maxparts')
    if not base:
        base = 60
    bases = _flatten([base])
    for b in bases:
        if not isinstance(b, int) or b < 2:
            raise AnsibleFilterError(f'base must consist of integers > 1')

    if value < 0:
        sign = "-"
        value = abs(value)
    else:
        sign = ""

    acc = []
    while not maxparts or len(acc) < maxparts:
        base = bases[-1]
        if len(bases) > 1:
            bases.pop()
        partfmt = "0" + f"0{len(f"{base-1}")}d"
        if maxparts and len(acc) == maxparts - 1:
            if minparts and minparts == maxparts:
                # zero-pad if minparts == maxparts
                acc.insert(0, f"{value:{partfmt}}")
            else:
                acc.insert(0, f"{value}")
            break
        acc.insert(0, f"{value % base:{partfmt}}")
        value //= base
        if value == 0:
            break

    if minparts:
        while len(acc) < minparts:
            base = bases[-1]
            if len(bases) > 1:
                bases.pop()
            partfmt = "0" + f"0{len(f"{base-1}")}d"
            acc.insert(0, f'{0:{partfmt}}')
    result_string = to_text(f"{sign}{':'.join(acc)}")
    return result_string

class FilterModule(object):
    ''' Ansible core jinja2 filters '''

    def filters(self):
        return {'int2sexagesimal': int2sexagesimal}

if __name__ == " __main__":
    for val in [86399, 3600, -500, 36125, 32000000, 8986232651, 338986232651, 216000, 'a string']:
        for ma in [None, 3, 5, 6]:
            for mi in [None, 4, 5, 6]:
                for ba in [None, [100, 365, 24, 60, 60]]:
                    if ma and mi and mi > ma:
                        break
                    print(f"{str(val):8s}: min:{str(mi):5s}, max:{str(ma):5s}, base:{str(ba):24s}) => {int2sexagesimal(val, minparts=mi, maxparts=ma, base=ba):17s}")
    # print(f" 86399: {int2sexagesimal( 86399 )}")
    # print(f" 86399: {int2sexagesimal( 86399, minparts=2)}")
    # print(f" 86399: {int2sexagesimal( 86399, maxparts=3)}")
    # print(f" 3600: {int2sexagesimal( 3600 )}")
    # print(f" -500: {int2sexagesimal( -500 )}")
    # print(f" 36125: {int2sexagesimal( 36125, minparts=3)}")
    # print(f"8986232651: {int2sexagesimal(8986232651, minparts=3)}")
    # print(f" 216000: {int2sexagesimal( 216000, minparts=3)}")
    # print(f"'a string': {int2sexagesimal('a string', minparts=8)}")

```

---

<div class="post-metadata">

**Author:** ![RianKellyIT](https://avatars.discourse-cdn.com/v4/letter/r/edb3f5/32.png) [@RianKellyIT](https://forum.ansible.com/u/RianKellyIT)\
**Post date:** [March 30, 2026, 8:22am UTC](https://forum.ansible.com/t/strange-behaviour-in-ansible-2-19-string-converted-to-an-int/45545/12 "2026-03-30T08:22:36Z")

</div>

This is classic YAML 1.1 sexagesimal behaviour. Any unquoted value matching the `nn:nn:nn:...` pattern where all components are decimal and under 60 gets parsed as a base-60 integer before Ansible ever sees it.

The root fix is quoting at the point of entry. In the AWX extra vars prompt, wrap the value in double quotes:

```auto
macadres: "11:33:22:55:44:11"

```

That tells the YAML parser to treat it as a string unconditionally. The reason Case 1 worked is that the job template variable was stored with quotes intact, whereas the prompt input in Case 2 strips outer quotes, leaving the bare value exposed to YAML 1.1 parsing.

For MAC addresses you’re already ingesting as integers, @utoddl’s `int2sexagesimal` filter from post #10 is the right recovery path. The `minparts=6, maxparts=6` combination is what you want for MAC addresses specifically, as it guarantees zero-padded 6-part output.

---

<div class="post-metadata">

**Author:** ![ildjarn](https://avatars.discourse-cdn.com/v4/letter/i/8491ac/32.png) [@ildjarn](https://forum.ansible.com/u/ildjarn)\
**Post date:** [March 30, 2026, 8:36am UTC](https://forum.ansible.com/t/strange-behaviour-in-ansible-2-19-string-converted-to-an-int/45545/13 "2026-03-30T08:36:05Z")

</div>

@RianKellyIT

See the screenshots. AWX does quote the extra\_vars, but it looks like variables that are injected with the ‘prompt on launch’ are treated differently.

---

<div class="post-metadata">

**Author:** ![nitzmahone](https://avatars.discourse-cdn.com/v4/letter/n/c5a1d2/32.png) [@nitzmahone](https://forum.ansible.com/u/nitzmahone)\
**Post date:** [April 2, 2026, 11:27pm UTC](https://forum.ansible.com/t/strange-behaviour-in-ansible-2-19-string-converted-to-an-int/45545/14 "2026-04-02T23:27:28Z")

</div>

Since this provides an opportunity to discuss a concrete example of a trip-hazard that’s been around, well, basically since YAML was created, I’m curious what y’all would think if we deprecated and eventually killed off some of those more problematic YAML 1.1 implicit resolvers in Ansible?

I suspect that back when YAML was originally integrated into Ansible, they didn’t know how to easily configure it not to use some of the more problematic default resolvers like sexagesimals, octals, and non `true/false` booleans, so ever since we’ve been stuck in this (too common) limbo where those are huge trip-hazards to _most_ users, but a handful actually rely on it. It’s relatively easy to do though, and we could stage it in such a way that it could fire explicit warnings/errors when those values are encountered or used. I know I’ve personally gotten nailed countless times by weird behavior from values like `0800` and `no`.

That particular set of changes would also be forward-compatible with the most common standard tag set in YAML 1.2 if/when we ever get there- the tiny parser-level differences between 1.1 and 1.2 are generally of little consequence to typical Ansible content anyway.

---

<div class="post-metadata">

**Author:** ![zendritic](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/zendritic/32/10412_2.png) [@zendritic](https://forum.ansible.com/u/zendritic)\
**Post date:** [September 14, 2026, 12:54pm UTC](https://forum.ansible.com/t/strange-behaviour-in-ansible-2-19-string-converted-to-an-int/45545/15 "2026-09-14T12:54:19Z")

</div>

I have a similar, likely the exact same issue. Mine is with Ascender 25.5.1 and octal, not sexagesimals. I have a variable that is expected to be a 4 digit string, composed entirely of decimals, EG `0700`. When saved as “Variables” (AWX for extra vars) in a job template, they end up in Ansible as a string. When extra vars are set to “prompt on launch”, regardless of the fact that _the value is quoted_, if the string has a leading 0, Ansible 2.19 parses it as octal, while 2.18 preserves it as a string. The same thing happens when using an AWX survey, even if the survey question is defined as “text”, which is supposed to preserve it as a string when passed to Ansible. However AWX/Ascender writes vars into the extra vars file works for this edge case with 2.18 but not with 2.19.
