Sửa lỗi Ansible 'item is a string and cannot be compared to an integer' trong điều kiện when

beginner🔧 Ansible2026-05-03| Ansible 2.9+, Python 3.x, bất kỳ control node Linux/macOS nào

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#biến#lỗi-kiểu-dữ-liệu

Lỗi Gặp Phải

Bạn đang lặp qua một danh sách hoặc dùng biến trong điều kiện when và Ansible báo lỗi:

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

Playbook dừng lại. Không có gì chạy được. Phần bực bội nhất? Giá trị trông như là số — chỉ là Jinja2 không đồng ý với bạn.

Nguyên Nhân

Biến trong Ansible có kiểu dữ liệu lỏng lẻo — và sự linh hoạt đó gây rắc rối ở đây. Định nghĩa một danh sách trong file vars, inventory, hoặc từ kết quả register, và các giá trị thường về dưới dạng chuỗi dù trông như số:

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

Jinja2 thấy "8080"5 là hai kiểu không tương thích. Python 3 — môi trường Ansible chạy trên đó — từ chối tự động ép kiểu chuỗi thành số khi so sánh. Python 2 bỏ qua điều này; Python 3 thì không.

Các nguồn phổ biến khiến số bị lưu dưới dạng chuỗi:

  • Giá trị YAML đặt trong dấu nháy: port: "8080"
  • Biến môi trường từ lookup('env', ...)
  • Kết quả lệnh được lưu qua register + stdout
  • Biến truyền qua --extra-vars trên CLI (luôn là chuỗi, không có ngoại lệ)
  • Các fact từ máy chủ như ansible_processor_count, tùy thuộc vào host

Chẩn Đoán Vấn Đề

Đầu tiên, xác nhận xem Ansible thực sự thấy kiểu dữ liệu gì. Thêm một task debug trước điều kiện đang lỗi:

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

Nếu kết quả hiển thị Type: str thay vì Type: int, đó chính là thủ phạm.

Kiểm tra một biến đơn lẻ cũng làm tương tự:

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

Cách Sửa 1: Ép Kiểu Bằng Filter int (Khuyến Nghị)

Dùng filter int — nó chuyển đổi chuỗi thành số nguyên ngay trong biểu thức when, không cần cấu trúc lại:

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

Việc ép kiểu xảy ra trước khi so sánh. Nếu một giá trị không thể chuyển đổi — ví dụ chuỗi rỗng — filter mặc định về 0, giúp tránh crash thay vì làm hỏng toàn bộ lần chạy.

Cần giá trị mặc định khác? Đặt rõ ràng:

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

Cách Sửa 2: Bỏ Dấu Nháy Trong Định Nghĩa Biến

Bạn có quyền kiểm soát trực tiếp định nghĩa biến? Hãy bỏ dấu nháy đi:

# Trước (chuỗi)
port_list:
  - "8080"
  - "443"

# Sau (số nguyên)
port_list:
  - 8080
  - 443

Số không có dấu nháy trong YAML luôn được phân tích là số nguyên. Dữ liệu nguồn sạch hơn đồng nghĩa với playbook đơn giản hơn — không cần lọc qua lọc lại ở phần sau.

Cách Sửa 3: Ép Kiểu Ngay Tại Điểm Register

Kết quả từ register luôn về dạng chuỗi. Hãy ép kiểu sớm — tốt nhất ngay trong điều kiện when nơi bạn sử dụng nó:

- 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

Nếu bạn sẽ dùng lại giá trị ở nhiều chỗ, hãy lưu kết quả đã ép kiểu vào một fact:

- 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

Cách Sửa 4: Biến Extra Vars Từ CLI

Chuỗi từ --extra-vars luôn là chuỗi — dù trông như thế nào đi nữa. Hãy ép kiểu bên trong playbook:

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

Ngoài ra, truyền giá trị dưới dạng JSON để giữ nguyên kiểu dữ liệu từ nguồn:

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

Kiểm Tra Sau Khi Sửa

Chạy với -v và quan sát kết quả task:

ansible-playbook site.yml -v

Khi sửa đúng, kết quả sẽ trông như thế này:

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

Không còn lỗi 'item' is a string nữa. Điều kiện bỏ qua 443 và chạy trên 8080, đúng như mong đợi.

Vẫn chưa chắc việc ép kiểu có hoạt động không? In kiểu dữ liệu ra sau filter:

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

Kết quả mong đợi: Casted type: int

Tham Khảo Nhanh

  • {{ item | int }} — ép kiểu chuỗi sang số nguyên (mặc định về 0 nếu thất bại)
  • {{ item | float }} — ép kiểu sang số thực nếu cần
  • {{ item | string }} — ép kiểu số nguyên trở lại thành chuỗi
  • {{ item | type_debug }} — in kiểu Python thực tế của biến
  • Số YAML không có dấu nháy (8080) là số nguyên; có dấu nháy ("8080") là chuỗi

Bài Học Rút Ra

Chín trong mười trường hợp, thủ phạm là số được đặt trong dấu nháy trong YAML hoặc giá trị được pipe từ lệnh shell. Cả hai đều về dạng chuỗi. Filter int xử lý được cả hai trường hợp tại chỗ — chỉ thêm một chút nhỏ, không cần cấu trúc lại playbook.

Một thói quen đáng xây dựng: ngừng đặt số trong dấu nháy ở file vars. YAML tự động xử lý 8080 như số nguyên. Hãy để nó làm việc đó, và một nửa các lỗi kiểu dữ liệu này sẽ biến mất trước khi chúng kịp xảy ra.

Related Error Notes