はじめに
Windowsのタスクスケジューラ(schtasks)から定期実行しているPythonスクリプトで、Discord Webhookによる通知を飛ばす仕組みを運用していました。手動で実行するとDiscordに届くのに、タスクスケジューラ経由で実行した場合だけ通知が届かないことがある、という現象に遭遇しました。
本記事では、この「たまに届かない」原因の調査手順と、恒久的な解決策として採用したバッチファイル経由の実行構成を紹介します。同様にタスクスケジューラからPythonスクリプトをバックグラウンド実行している場合の参考になれば幸いです。
発生していた現象
- タスクスケジューラのタスク実行結果(成功ログ)自体は「正常終了」を示している。
- にもかかわらず、Discord側にWebhook通知が届かないことがある。
- 手動でスクリプトを実行すると問題なく届く。
タスク自体はエラーなく終了しているように見えるため、最初は原因の切り分けが難しい状態でした。
調査:schtasksの成功ログとstate.jsonの更新差分を突き合わせる
タスクスケジューラの実行結果ログと、スクリプト内部で更新している状態ファイル(state.json)のタイムスタンプを突き合わせて確認したところ、次のことが分かりました。
- タスクスケジューラの実行タスクは
wscript.exe経由で.vbsスクリプトを呼び出し、そこからpythonw.exeを非表示で起動する構成になっていた。 -
wscript.exeからpythonw.exeを直接呼び出す構成では、プロセスの起動自体が確実にキックされない(起動漏れが発生する)ケースがあることを特定した。
つまり「タスクスケジューラのタスクとしては成功扱いになるが、その中で本来起動すべきPythonプロセス自体が実行されずに終わっている」ケースがあり、これが通知の欠落として現れていました。
対処:バッチファイルを1枚挟んでプロセス起動を確実にする
wscript.exe から pythonw.exe を直接叩く構成をやめ、間にバッチファイルを1枚挟む構成に変更しました。
-
run_heartbeat.batを新規作成-
.envの読み込みと、実行結果のログ出力(heartbeat.logへの統合)をバッチファイル内で行う。
-
-
run_heartbeat.vbsの呼び出し先を変更-
pythonw.exeを直接呼んでいた処理を、run_heartbeat.batを非表示(ウィンドウ非表示モード)で呼び出す形に変更。
-
構成をまとめると、以下のような呼び出し階層になります。
タスクスケジューラ (schtasks)
└─ run_heartbeat.vbs(非表示実行のラッパー)
└─ run_heartbeat.bat(.env読込 & ログ出力を統合)
└─ pythonw.exe(本体スクリプト)
バッチファイルを経由させることで、環境変数の読み込みとログ出力を一箇所に集約でき、かつプロセスの起動自体も安定するようになりました。
検証
schtasks /run でタスクを手動実行し、heartbeat.log に Notification Status: True が出力されることを確認しました。あわせてDiscord側にも通知が正常に着信することを確認し、対処が有効であることを確認しました。
まとめ・他の運用にも応用できるポイント
- 「タスク成功」と「中身の処理が実行された」は別物:タスクスケジューラの成功ログだけを見て安心せず、スクリプト内部の状態ファイルやログのタイムスタンプで実処理の実行有無を検証する。
-
wscript.exeから実行ファイルを直接叩く構成は起動漏れのリスクがある:非表示実行のためにVBScriptラッパーを使う構成自体はよくあるが、直接実行ファイルを呼ぶのではなく、バッチファイルを1枚挟んでログ出力・環境変数読込・起動確認を担わせると安定しやすい。 - 通知系の処理は「届かないことがある」前提でログを仕込む:Webhook送信結果(成功/失敗)を必ずログに残しておくと、こうした「たまに起きる」系の不具合の切り分けが格段に楽になる。
バックグラウンド実行かつ通知を伴うタスクスケジューラ運用をしている場合、同様の構成の見直しが有効なケースがあるかもしれません。