Sửa lỗi Ansible "The requested handler was not found" khi dùng notify

intermediate🔧 Ansible2026-04-27| Ansible 2.9+, Ubuntu/CentOS/Debian, mọi playbook dùng notify + handlers

Error Message

ERROR! The requested handler 'restart nginx' was not found in either the main handlers list nor in the listening handlers list
#ansible#handler#notify#playbook

Lỗi Gặp Phải

Bạn chạy playbook Ansible và nó dừng lại với thông báo:

ERROR! The requested handler 'restart nginx' was not found in either the main handlers list nor in the listening handlers list

Playbook chạy tốt hôm qua. Bạn không chạm vào handler. Vậy mà Ansible vẫn không tìm thấy nó.

Nguyên Nhân

Ansible so khớp tên handler theo chuỗi ký tự chính xác — từng dấu cách, từng chữ hoa đều có ý nghĩa. Giá trị notify phải giống hệt từng byte với trường name của handler. Chỉ một dấu cách thừa ở cuối hoặc một chữ hoa sai vị trí là đủ để gây lỗi.

Có bốn tình huống riêng biệt có thể kích hoạt lỗi này:

  • Tên không khớp — lỗi đánh máy, sai chữ hoa/thường, hoặc có dấu cách thừa đầu/cuối
  • Handler định nghĩa trong một role nhưng notify lại nằm trong playbook thường (hoặc ngược lại)
  • File handler chưa được import — handlers/main.yml bị thiếu hoặc chưa được include
  • Handler nằm trong một block không bao giờ được thực thi, nên Ansible không bao giờ đăng ký nó

Cách Khắc Phục Từng Bước

1. Sao chép-dán tên handler — đừng gõ lại

Mở file chứa handler. Sao chép chính xác chuỗi từ trường name của nó. Dán vào notify. Đừng gõ lại từ trí nhớ.

# handlers/main.yml
- name: restart nginx          # ← copy chính xác chuỗi này
  ansible.builtin.service:
    name: nginx
    state: restarted
# tasks/main.yml
- name: Deploy nginx config
  ansible.builtin.template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
  notify: restart nginx         # ← dán vào đây, phải giống hệt

Những cách viết dưới đây trông có vẻ đúng nhưng sẽ làm hỏng playbook:

notify: "restart nginx "    # dấu cách thừa ở cuối — SAI
notify: "Restart nginx"     # chữ R hoa — SAI
notify: "restart  nginx"    # hai dấu cách — SAI
notify: restart nginx       # đúng (dấu nháy không bắt buộc)

2. Kiểm tra handler có đúng phạm vi không

Handler nằm trong một role chỉ hiển thị với các task trong chính role đó. Một task ở playbook cấp cao không thể notify handler nằm trong roles/myrole/handlers/main.yml — Ansible đơn giản là không nhìn thấy nó.

Nếu notify của bạn nằm trong task của playbook thường, handler phải thuộc khối handlers của chính playbook đó:

- hosts: webservers
  handlers:
    - name: restart nginx
      ansible.builtin.service:
        name: nginx
        state: restarted

  tasks:
    - name: Deploy nginx config
      ansible.builtin.template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      notify: restart nginx

Handler nằm trong role? Task của bạn cũng phải nằm trong role đó — hoặc dùng tính năng listen được mô tả bên dưới.

3. Đảm bảo handlers/main.yml thực sự được tải

Với roles, Ansible tự động tải roles/rolename/handlers/main.yml. Các file handler tùy chỉnh thì khác — bạn phải include chúng một cách tường minh, nếu không Ansible sẽ bỏ qua hoàn toàn.

# Sai: file handler tồn tại nhưng không bao giờ được tải
- hosts: webservers
  tasks:
    - name: Deploy config
      ansible.builtin.template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      notify: restart nginx
# Đúng: import file handler
- hosts: webservers
  handlers:
    - ansible.builtin.import_tasks: handlers/nginx.yml

  tasks:
    - name: Deploy config
      ansible.builtin.template:
        src: nginx.conf.j2
        dest: /etc/nginx/nginx.conf
      notify: restart nginx

4. Dùng listen để vượt qua ranh giới role một cách gọn gàng

Cần notify qua các role khác nhau? Dùng listen. Thay vì khớp theo name của handler, notify sẽ khớp với nhãn listen mà bạn định nghĩa riêng:

# roles/nginx/handlers/main.yml
- name: Restart the nginx service
  ansible.builtin.service:
    name: nginx
    state: restarted
  listen: "restart nginx"    # ← notify khớp với nhãn này
# Bất kỳ task nào trong play
- name: Update nginx config
  ansible.builtin.template:
    src: nginx.conf.j2
    dest: /etc/nginx/nginx.conf
  notify: "restart nginx"    # khớp với nhãn listen, không phải tên handler

Cách này cũng cho phép nhiều handler cùng phản hồi một thông báo. Và nếu bạn đổi tên handler nội bộ, các task của bạn không bị ảnh hưởng — chúng gắn với nhãn, không phải tên.

5. Debug với --syntax-check và -v trước khi chạy

Xác nhận Ansible có thể phân tích cú pháp handler của bạn mà không cần thực sự chạy gì:

ansible-playbook site.yml --syntax-check

Liệt kê toàn bộ task và handler sẽ được tải:

ansible-playbook site.yml --list-tasks

Chạy với output chi tiết để theo dõi quá trình đăng ký handler theo thời gian thực:

ansible-playbook site.yml -v

Để ý các dòng như NOTIFIED HANDLER restart nginx. Nếu thấy dòng đó, tên đã khớp đúng.

Kiểm Tra Kết Quả

Sau khi đã sửa, hãy chạy dry-run với chế độ verbose:

ansible-playbook site.yml --check -v

Ansible vẫn phân giải tên handler trong chế độ check — không có thay đổi thực nào xảy ra, nhưng các lỗi không khớp vẫn hiện ra. Một lần chạy thành công trông như thế này:

TASK [Deploy nginx config] ***********************
changed: [webserver1]

RUNNING HANDLER [restart nginx] ******************
changed: [webserver1]

Không có thông báo ERROR! The requested handler nghĩa là bạn đã xong.

Tham Khảo Nhanh: Các Tình Huống Thường Gặp

  • Lỗi đánh máy trong notify → sao chép-dán nguyên văn tên handler, đừng gõ lại
  • Handler trong role, task trong playbook → chuyển handler vào playbook hoặc dùng listen
  • File handler chưa được tải → thêm import_tasks hoặc định nghĩa handler trực tiếp trong khối handlers:
  • Lỗi trên một số host nhưng không phải tất cả → kiểm tra xem khối handler có nằm trong điều kiện when bị đánh giá là false trên một số host không
  • Sau khi tái cấu trúc roles → kiểm tra lại từng giá trị notify thủ công — chúng không tự cập nhật khi bạn đổi tên handler

Tại Sao Ansible Lại Chặt Chẽ Về Điều Này

Đây là thiết kế có chủ đích. Một handler kích hoạt nhầm server còn nguy hiểm hơn nhiều so với một lỗi thông báo ầm ĩ. Việc so khớp tên chặt chẽ buộc bạn phải rõ ràng và tường minh.

Với bất cứ điều gì vượt qua ranh giới role, hãy áp dụng listen làm mặc định. Nó tách rời tên nội bộ của handler khỏi nhãn sự kiện mà các task tham chiếu — thoải mái đổi tên hoặc tách handler mà không bao giờ làm hỏng một notify nào.

Related Error Notes