Ansibleの「The requested handler was not found」エラーをnotify使用時に修正する方法

intermediate🔧 Ansible2026-04-27| Ansible 2.9+、Ubuntu/CentOS/Debian、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#ハンドラー#notify#プレイブック

エラーの内容

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を壊すことなくハンドラーを自由に名前変更したり分割したりできます。

Related Error Notes