エラーの内容
Ansibleのplaybookを実行すると、次のエラーで中断されます:
ERROR! The requested handler 'restart nginx' was not found in either the main handlers list nor in the listening handlers list
昨日まで正常に動いていたplaybookです。ハンドラーには手を加えていません。それでもAnsibleはハンドラーを見つけられません。
根本原因
Ansibleはハンドラー名を完全一致の文字列として照合します。スペース一つ、大文字小文字一つに至るまですべてが重要です。notifyの値は、ハンドラーのnameフィールドとバイト単位で完全に一致しなければなりません。末尾の余分なスペースや、本来小文字であるべき箇所の大文字一つだけで動作が壊れます。
このエラーが発生する典型的な状況は4つあります:
- 名前の不一致 — タイポ、大文字小文字の違い、または先頭・末尾のスペース
- ハンドラーがロール内で定義されているが、
notifyが通常のplaybook内にある(またはその逆) - ハンドラーファイルが読み込まれていない —
handlers/main.ymlが存在しないか、インクルードされていない - ハンドラーが一度も実行されない
blockの中にあり、Ansibleが登録できない
手順ごとの修正方法
1. ハンドラー名はコピー&ペーストする — 手で打ち直さない
ハンドラーが定義されているファイルを開き、nameフィールドの文字列をそのままコピーします。それをnotifyに貼り付けます。記憶を頼りに打ち直してはいけません。
# handlers/main.yml
- name: restart nginx # ← この文字列をそのままコピーする
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 # ← ここに貼り付ける。完全に一致している必要がある
一見正しく見えますが、以下はすべてplaybookを壊します:
notify: "restart nginx " # 末尾のスペース — 誤り
notify: "Restart nginx" # 大文字のR — 誤り
notify: "restart nginx" # スペース二つ — 誤り
notify: restart nginx # 正しい(クォートは省略可)
2. ハンドラーが正しいスコープにあるか確認する
ロール内のハンドラーは、同じロール内のタスクからのみ参照できます。トップレベルのplaybookのタスクからroles/myrole/handlers/main.ymlにあるハンドラーを呼び出すことはできません。Ansibleはそのハンドラーを認識しません。
通常のplaybookタスクでnotifyを使う場合、ハンドラーはそのplaybookのhandlersブロック内に定義する必要があります:
- 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
ハンドラーがロール内にある場合、タスクも同じロール内に置く必要があります。あるいは、後述のlisten機能を使用してください。
3. handlers/main.yml が実際に読み込まれているか確認する
ロールの場合、Ansibleはroles/rolename/handlers/main.ymlを自動的に読み込みます。カスタムのハンドラーファイルは別の話です。明示的にインクルードしなければ、Ansibleは完全に無視します。
# 誤り: ハンドラーファイルは存在するが読み込まれていない
- hosts: webservers
tasks:
- name: Deploy config
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: restart nginx
# 正しい: ハンドラーファイルを明示的にインポートする
- 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. listen を使ってロールの境界をまたいで呼び出す
ロールをまたいで通知が必要な場合はlistenを使います。ハンドラーのnameに一致させる代わりに、notifyは別途定義したlistenラベルに一致します:
# roles/nginx/handlers/main.yml
- name: Restart the nginx service
ansible.builtin.service:
name: nginx
state: restarted
listen: "restart nginx" # ← notifyはこのラベルに一致する
# play内のどこにあるタスクでも
- name: Update nginx config
ansible.builtin.template:
src: nginx.conf.j2
dest: /etc/nginx/nginx.conf
notify: "restart nginx" # ハンドラー名ではなくlistenラベルに一致する
この方法では、複数のハンドラーが一つの通知に応答することもできます。また、ハンドラーの内部名を変更しても、タスクは壊れません。タスクはハンドラー名ではなくラベルに紐づいているからです。
5. 実行前に --syntax-check と -v でデバッグする
実際に実行せずに、Ansibleがハンドラーを正しく解析できるか確認します:
ansible-playbook site.yml --syntax-check
読み込まれるすべてのタスクとハンドラーを一覧表示します:
ansible-playbook site.yml --list-tasks
詳細出力で実行し、ハンドラーの登録をリアルタイムで確認します:
ansible-playbook site.yml -v
NOTIFIED HANDLER restart nginxのような行が表示されれば、名前が正しく一致しています。
動作確認
修正が完了したら、詳細出力付きでドライランを実行します:
ansible-playbook site.yml --check -v
Ansibleはチェックモードでもハンドラー名を解決します。実際の変更は行われませんが、名前の不一致は検出されます。正常に実行された場合の出力は次のようになります:
TASK [Deploy nginx config] ***********************
changed: [webserver1]
RUNNING HANDLER [restart nginx] ******************
changed: [webserver1]
ERROR! The requested handlerのメッセージが表示されなければ、修正完了です。
クイックリファレンス:よくあるシナリオ
- notifyにタイポがある → ハンドラー名をそのままコピー&ペーストし、絶対に手で打ち直さない
- ハンドラーがロール内にあり、タスクがplaybookにある → ハンドラーをplaybookに移動するか、
listenを使用する - ハンドラーファイルが読み込まれていない →
import_tasksを追加するか、handlers:の下にハンドラーをインラインで定義する - 一部のホストでのみ失敗する → ハンドラーブロックが特定のホストでfalseになる
when条件の中にないか確認する - ロールをリファクタリング後 → すべての
notifyの値を手動で確認する — ハンドラー名を変更しても自動的に更新されない
Ansibleがこれほど厳密な理由
これは意図的な設計です。誤ったサーバーで発動するハンドラーは、大きなエラーをスローするよりもはるかに危険です。名前の厳密な一致によって、明示的に記述することが強制されます。
ロールをまたぐ場合は、listenをデフォルトとして採用してください。ハンドラーの内部名をタスクが参照するイベントラベルから切り離せるため、notifyを壊すことなくハンドラーを自由に名前変更したり分割したりできます。

