Ansibleの「itemは文字列であり、整数と比較できません」エラーをwhen条件で修正する

beginner🔧 Ansible2026-05-03| Ansible 2.9以降、Python 3.x、Linux/macOSコントロールノード

Error Message

The conditional check 'item > 5' failed. The error was: error while evaluating conditional (item > 5): 'item' is a string and cannot be compared to an integer
#ansible#jinja2#変数#型エラー

エラーの内容

リストをループしたり、when条件で変数を使用したりすると、Ansibleが以下のエラーをスローします:

The conditional check 'item > 5' failed. The error was: error while evaluating conditional (item > 5): 'item' is a string and cannot be compared to an integer

プレイブックは停止し、何も実行されません。厄介なのは、値が数字に見えるのに、Jinja2はそう認識しないことです。

発生原因

Ansibleの変数は型が緩やかで、その柔軟性がここで問題を引き起こします。varsファイル、インベントリ、またはregisterの結果でリストを定義すると、数字のように見えても値は文字列として届くことがよくあります:

vars:
  port_list:
    - "8080"
    - "443"
    - "3000"

Jinja2は"8080"5を互換性のない型として見なします。AnsibleがベースにしているPython 3は、比較時に文字列を数値に暗黙的に変換することを拒否します。Python 2ではそれが許されていましたが、Python 3では許されません。

文字列型の数値が生まれる主な原因:

  • クォートで囲まれたYAMLの値:port: "8080"
  • lookup('env', ...)から取得した環境変数
  • register + stdoutで取得したコマンド出力
  • CLI経由で--extra-varsとして渡された変数(例外なく常に文字列)
  • ホストによってはansible_processor_countなどのリモートファクト

問題の診断

まず、Ansibleが実際にどの型として認識しているか確認しましょう。失敗している条件の前にデバッグタスクを追加します:

- name: Debug item type
  debug:
    msg: "Value: {{ item }} | Type: {{ item | type_debug }}"
  loop: "{{ port_list }}"

出力にType: intではなくType: strと表示されていれば、それが原因です。

単一の変数の確認も同じ方法で行えます:

- name: Check variable type
  debug:
    msg: "{{ my_count | type_debug }}"

修正方法1:intフィルターでキャストする(推奨)

intフィルターを使用します。when式の中で文字列を整数に変換できるため、プレイブックの構造を変更する必要はありません:

- name: Only process ports above 1024
  debug:
    msg: "High port: {{ item }}"
  loop: "{{ port_list }}"
  when: item | int > 1024

比較が実行される前にキャストが行われます。空文字列など変換できない値があっても、フィルターはデフォルトで0を返すため、実行が中断されることなくクラッシュを防げます。

別のデフォルト値が必要な場合は明示的に設定できます:

when: item | int(default=0) > 1024

修正方法2:変数定義からクォートを削除する

変数定義を直接制御できる場合は、クォートを削除してください:

# 修正前(文字列)
port_list:
  - "8080"
  - "443"

# 修正後(整数)
port_list:
  - 8080
  - 443

YAMLではクォートなしの数値は常に整数として解析されます。ソースデータをきれいにしておくことで、プレイブックがシンプルになり、フィルターの操作が不要になります。

修正方法3:register時点でキャストする

registerの結果は常に文字列として返ってきます。早めにキャストしましょう。理想的には使用するwhen条件の中でキャストします:

- name: Get number of running containers
  command: docker ps -q | wc -l
  register: container_count

- name: Warn if too many containers
  debug:
    msg: "Container count is high!"
  when: container_count.stdout | int > 10

複数の場所で値を再利用する場合は、キャスト結果をファクトに保存しましょう:

- name: Set integer fact
  set_fact:
    container_count_int: "{{ container_count.stdout | int }}"

- name: Use integer fact in condition
  debug:
    msg: "High count: {{ container_count_int }}"
  when: container_count_int > 10

修正方法4:CLIからのextra-vars

--extra-varsの文字列は見た目にかかわらず常に文字列です。プレイブック内でキャストしてください:

ansible-playbook site.yml --extra-vars "max_retries=5"
- name: Retry check
  debug:
    msg: "Too many retries"
  when: max_retries | int > 3

あるいは、ソース時点で型を保持するためにJSON形式で値を渡すこともできます:

ansible-playbook site.yml --extra-vars '{"max_retries": 5}'

修正の確認

-vオプションで実行し、タスクの出力を確認します:

ansible-playbook site.yml -v

修正が成功した場合の出力はこのようになります:

TASK [Only process ports above 1024]
skipping: [localhost] => (item=443)
ok: [localhost] => (item=8080)

'item' is a stringエラーはもう表示されません。条件は443をスキップし、8080で実行されます。意図した通りです。

キャストが正しく適用されたか確認したい場合は、フィルター後に型を明示的に出力します:

- name: Confirm type after cast
  debug:
    msg: "Casted type: {{ item | int | type_debug }}"
  loop: "{{ port_list }}"

期待される出力:Casted type: int

クイックリファレンス

  • {{ item | int }} — 文字列を整数にキャスト(失敗時のデフォルトは0)
  • {{ item | float }} — 必要に応じて浮動小数点数にキャスト
  • {{ item | string }} — 整数を文字列に戻してキャスト
  • {{ item | type_debug }} — 実際のPythonの型を出力
  • クォートなしのYAML数値(8080)は整数、クォートあり("8080")は文字列

まとめ

ほとんどの場合、原因はYAMLの中でクォートされた数値か、シェルコマンドからパイプされた値です。どちらも文字列として届きます。intフィルターはどちらのケースにも一行で対応できます。プレイブックを大きく変える必要はありません。

身につけておきたい習慣があります。varsファイルで数値をクォートするのをやめることです。YAMLは8080を自動的に整数として扱います。それに任せることで、この種の型エラーの半分は発生する前に消えていきます。

Related Error Notes