Ansible Error Guide: 'unable to convert to int' — Fix Argument Type Mismatches
Fix Ansible's 'we were unable to convert to int' argument error by casting templated strings, defaulting empty vars, and matching module argument types.
- #ansible
- #automation
- #troubleshooting
- #errors
Fixing errors like this? Get 500 free DevOps AI prompts
500 copy-paste AI prompts for the stack you actually run — one PDF, free.
Overview
Ansible raises this during argument validation when a module argument declared as an integer receives a value it cannot coerce to one:
fatal: [app01]: FAILED! => {"changed": false, "msg": "argument 'mode' is of type <class 'str'> and we were unable to convert to int: <class 'str'> cannot be converted to an int"}
A common templated variant looks like this, where a variable resolved to something non-numeric:
fatal: [app01]: FAILED! => {"changed": false, "msg": "argument 'port' is of type <class 'str'> and we were unable to convert to int: invalid literal for int() with base 10: ''"}
The module never executed. Ansible type-checks arguments first, and a value that cannot become the declared type stops the task before any change is made.
Symptoms
- A task fails with
is of type <class 'str'> and we were unable to convert to int. - The failure references an empty string (
''), meaning a variable resolved to nothing. - A value that “looks like a number” fails because it is a string with a stray character or whitespace.
- The same task works when the value is hard-coded but fails when templated from a variable.
- A
mode/port/timeout/retriesargument is the one named in the message.
Common Root Causes
- Undefined or empty variable —
port: "{{ svc_port }}"wheresvc_portis unset, resolving to''. - String that is not purely numeric — a value like
"8080 "with whitespace,"8_080", or"eight". - Wrong source type — a value read from JSON/CSV/
registerstdout that carries quotes or newlines. - Missing
| intfilter — a templated numeric string that the module expects already cast. - Octal confusion on
mode— passing file mode as an unquoted or malformed value the module cannot interpret. - Boolean where int expected — YAML coercing a value to
True/False.
Diagnostic Workflow
Print the resolved value and its type before the failing task so you can see what the module actually receives:
- name: Inspect the value being passed
ansible.builtin.debug:
msg: "port=>>{{ svc_port | default('UNDEFINED') }}<< type={{ svc_port | type_debug }}"
- name: Start the app with a numeric port
ansible.builtin.command:
cmd: "start-app --port {{ svc_port | int }}" # cast the string to int explicitly
Run the play with verbosity to see the constructed argument dictionary:
ansible-playbook -i inventory app.yml --limit app01 -vvv
Confirm the module’s expected argument type in the docs:
ansible-doc ansible.builtin.wait_for | grep -A4 -i 'port\|timeout'
Reproduce with a deliberately non-numeric value to validate the cast fixes it:
ansible app01 -i inventory -m wait_for -a "port='' timeout=30"
# msg: ... unable to convert to int: invalid literal for int() with base 10: ''
Example Root Cause Analysis
A deploy play failed intermittently with argument 'port' ... unable to convert to int: invalid literal for int() with base 10: ''. It only failed for a subset of hosts. Adding a debug with type_debug showed svc_port resolving to an empty string on exactly those hosts: the variable was defined in a host_vars file that existed for most hosts but was missing for the failing ones, so svc_port fell through to undefined and templated to ''.
The fix had two parts. First, svc_port: "{{ svc_port | default(8080) | int }}" gave a sane default and an explicit cast so a missing value no longer produced an empty string. Second, an assert at the top of the play validated that svc_port was defined and numeric, turning a mid-play type error into a clear pre-flight failure. The empty-string case is the tell here: it almost always means an undefined variable, not a genuinely non-numeric value.
Prevention Best Practices
- Apply
| int(or| float) explicitly to templated numeric values so the module receives the type it declares. - Provide
| default(<n>)for optional numeric variables so an unset value becomes a number, not an empty string. - Add an
assertearly in the play validating that required numeric vars aredefinedand match^[0-9]+$. - Use
type_debugin a temporarydebugtask to see the real type when a conversion fails. - Quote file
modevalues consistently and pass them in the form the module documents.
Quick Command Reference
# Show the resolved value and its type
- debug: msg="{{ myvar | default('UNDEF') }} :: {{ myvar | type_debug }}"
# Run with the argument dict visible
ansible-playbook -i inventory play.yml --limit <host> -vvv
# Check a module argument's expected type
ansible-doc <module> | grep -A4 -i <argument>
# Reproduce the conversion failure
ansible <host> -i inv -m wait_for -a "port='' timeout=30"
Conclusion
“unable to convert to int” is a type-check failure, not a logic bug: a module argument declared as an integer got a value it could not coerce. When the message shows an empty string, suspect an undefined variable first. Cast templated numerics with | int, default optional ones, and assert required vars up front so a type mismatch fails loudly at pre-flight instead of deep inside a run. Match the value to the argument’s declared type and the task proceeds.
More Ansible prompts & error guides
Every Ansible AI prompt and troubleshooting guide, in one place.
Get 500 Battle-Tested DevOps AI Prompts — Free
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.