タスクスケジューラの失敗に気づく方法 ― 自動実行を毎朝一覧で確かめる
自動実行を増やしていくと、怖くなるのは「失敗すること」より、「止まっているのに気づかないこと」です。失敗は画面やログに出ますが、動いていないタスクは何も出しません。数日たって「そういえば最近、あの記録が増えていない」と気づく、という形で発覚します。
この記事では、私が自宅のPCで自動実行を増やしたときに作った、毎朝の見張りの考え方をまとめます。動かし方そのものはWindowsのタスクスケジューラでClaude Codeを毎日自動で動かすに書いたので、ここでは「止まっていないかの確かめ方」に絞ります。
見張りの流れ
- タスクの状態を読むGet-ScheduledTask と Get-ScheduledTaskInfo
- 3つの目で判定する前回の結果コード / 最終実行時刻 / 有効か無効か
- 意図して止めたものを除く一覧の側に「止めてよい」印を持たせる
- 失敗だけを書き残す同じ失敗は1日1回までにする
見るべきなのは3つ
タスクが「ちゃんと動いている」とは、次の3つがそろっている状態だと考えています。
- 前回の結果コードが成功を示している。前回動いたときに、うまく終わったか。
- 最終実行時刻が古すぎない。毎日動くはずのタスクの最終実行が3日前なら、結果コードが成功でも「止まっている」と疑います。
- 無効になっていない。無効のタスクは、そもそも動きません。
結果コードだけを見ていると、「前回は成功したが、その後ずっと動いていない」タスクを見逃します。私の環境の一覧は、主に結果コードで成功か失敗かを判定しています。最終実行時刻と無効の確認は、後から足したほうがよいと考えている点です。
PowerShellで一覧を出す最小の例
タスクの定義は Get-ScheduledTask、実行の履歴は Get-ScheduledTaskInfo で読めます。Microsoftの説明では、後者は「スケジュールされたタスクの、最後の実行時の情報」を取るコマンドで、Get-ScheduledTask の出力をパイプでそのまま渡せます。出力には LastRunTime(最終実行時刻)、LastTaskResult(前回の結果)、NextRunTime(次回の実行時刻)、NumberOfMissedRuns(実行されなかった回数)があります。タスクが有効か無効かは、Get-ScheduledTask の State に出ます。
$names = "DailyTasks", "NightlyBackup" # 見張りたいタスク名
Get-ScheduledTask -TaskName $names | ForEach-Object {
$info = $_ | Get-ScheduledTaskInfo
[pscustomobject]@{
Name = $_.TaskName
State = $_.State
Last = $info.LastRunTime
Result = '0x{0:X}' -f [uint32]$info.LastTaskResult
Next = $info.NextRunTime
Missed = $info.NumberOfMissedRuns
}
} | Format-Table -AutoSize
出力の Result は16進数で表示しています。LastTaskResult をそのまま出すと10進数になり、次の表と見比べにくいためです。Microsoftの説明にある Get-ScheduledTask の出力例では、State は Ready、Disabled、Running のように表示されます。
結果コードの読み方
Microsoftが公開しているタスクスケジューラの定数のうち、一覧でよく出会うものを抜き出します。
| 16進数 | 10進数 | 意味 |
|---|---|---|
| 0x0 | 0 | エラーなく終わった(成功) |
| 0x41300 | 267008 | 次の予定の時刻に実行できる状態 |
| 0x41301 | 267009 | いま実行中 |
| 0x41302 | 267010 | 無効になっているので、予定の時刻には動かない |
| 0x41303 | 267011 | まだ一度も実行されていない |
| 0x41306 | 267014 | 前回の実行が、ユーザーによって終了された |
| 0x41325 | 267045 | タスクスケジューラが、タスクに実行を依頼した |
0以外の行は、Microsoftの定数一覧にある説明です。Microsoftの説明では、定数の名前が SCHED_S_ で始まるものは成功の定数、SCHED_E_ で始まるものはエラーの定数です。この表の0以外の定数は、すべて SCHED_S_ の側です。0x41301(実行中)と0x41303(未実行)は、一覧を作った直後に見ると「失敗」に見えがちですが、実際には失敗ではありません。私の最初の一覧は、登録したばかりのタスクの 0x41303 を失敗と表示してしまいました。この2つは「失敗」ではなく「未実行」「実行中」として別に扱うようにしています。
0x41306 は、説明には「ユーザーが終了した」とあります。私の環境では、タスクの「実行時間の制限」で止まったときにも同じ値が返りました。毎日一定の時刻に止める設計のタスクなら、これが正常な終わり方になります。ただ、固まって時間切れになったタスクでも同じ値が出るので、「このタスクに限って正常」と一覧の側で個別に指定し、他は失敗として扱っています。
スクリプトが exit 1 のように0以外で終わった場合は、私の環境ではその値が LastTaskResult にそのまま入っていました。前の記事で書いた exit $code を入れておくと、ここで失敗が見えます。
「動いていない」から、意図して止めたものを除く
一覧を作ると、必ず「無効のタスク」が混ざります。自分で止めたものもあれば、何かの拍子に止まったものもあります。ここで、無効なタスクを全部「異常」と報告すると、見張りへの信頼が落ちます。逆に、止まっていても気にしない癖がつくと、見張りの意味がなくなります。
私の決めごとは、「失敗や未実行と報告する前に、自分で意図的に止めたものでないかを確かめる」です。一覧の設定ファイルに、止めたタスクの印と理由を残しておくと、確かめる手間が省けます。止めた理由が分かれば、数か月後の自分も迷いません。
もうひとつ、ノートPCを持ち歩く場合の注意があります。私の環境では、バッテリー駆動中は開始しない設定のタスクが、電源につないでいない間ずっと Queued(実行待ち)のままになり、動きませんでした。このとき最終実行時刻が古くなるので、「止まった」ように見えます。壊れたのではなく、電源に戻れば動くので、PCの使い方と合わせて見る必要があります。この設定は動かし方の記事の表にまとめてあります。
失敗に気づく仕組み
私は次の3段にしています。
- 毎朝の一覧。上のようなスクリプトを定期的に(私は30分おきに)動かし、結果をHTMLなどの1ページにまとめます。眺めるだけで全体が分かります。
- 失敗だけを書き残す。失敗と判定したものだけをNotionのタスクとして作ります。毎日見る場所に失敗が並ぶので、一覧のページを開き忘れても気づけます。Notionへの書き込みは、標準ライブラリだけの小さなPythonで十分でした。定期的に動く見張りがAIを呼び出すと、そのたびに費用がかかるためです。
- 同じ失敗を何度も通知しない。30分おきに動かすと、同じ失敗を1日に何十回も見つけます。そのまま通知すると、タスクが山になります。そこで「どのタスクを、どの日に通知したか」を小さなファイルに記録し、同じ日の2回目以降は通知しない形にしました。翌日も直っていなければ、また1件作られます。
通知を絞る工夫は、失敗が続いたときに効きます。通知が多すぎると、本当に大事な1件を見落とします。
まとめ
- 自動実行で怖いのは、失敗よりも「止まっていることに気づかない」こと
- 見るのは、前回の結果コード、最終実行時刻、有効か無効かの3つ
Get-ScheduledTaskとGet-ScheduledTaskInfoで、状態・最終実行・結果コードを一覧にできる- 0x41301(実行中)と0x41303(未実行)は失敗ではない。結果コードは10進数でも出るので、表で見比べる
- 「止まっている」と報告する前に、意図して止めたものでないかを確かめる
- 失敗だけを書き残し、同じ失敗は1日1回までにする
仕組みを作る側として次に気をつけたいのは、Pythonの仮想環境やブラウザが気づかないうちに消えて、自動実行が静かに失敗することです。OneDriveの中にPythonの仮想環境を置くと壊れる話や、Playwrightのブラウザが消える話も、自動実行が静かに失敗する例です。