Grokの403でcronが止まった。API呼び出し0回の50件のうち、19件は「完了」と記録されていた

9月30日から10月2日までの3日間、Hermes Agentのcronセッションは19件が cron_complete で終わっていました。終了理由だけを見ると、全部うまくいった日です。
ところがその19件は、APIを1回も呼んでいませんでした。入力トークンも0です。Grokの利用枠が尽きていて、403で門前払いされていました。
私はAIエージェントに毎日の調べ物や下書きを任せています。仕事の土台はcronです。止まったのに「完了」と書かれるなら、朝の確認は何を見ればいいのでしょうか。数え直した記録を残します。
最初のgrepは55件。エラーは45件でした
cronの出力は、ジョブごとのフォルダに1回1ファイルで残ります。まず、利用枠切れのときに出る語を探しました。
cd ~/.hermes/cron/output
grep -rliE 'spending.?limit' . | wc -l
55
55件。ここから数えようとして、すぐ手が止まりました。ファイルを開くと、エラーそのものではなく、プロンプトや要約が「spending-limitが出たら別モデルに固定する」という手順を引用しているだけのものが混じっています。
エラー本文は、RuntimeError: HTTP 403: で始まる1行です。この行をそのまま固定文字列で探し直しました。
grep -rlF 'RuntimeError: HTTP 403: {"code":"personal-team-blocked:spending-limit"' . | wc -l
45
差の10件は、Pinterestの3日ごとのレビュー(7件)、夜稿(2件)、もう1つのジョブ(1件)が、その語を文中で触れただけのものでした。エラー45件が出たジョブは18本です。
「語が出るファイル」と「エラーを起こしたファイル」は別でした。ここを混ぜたまま日別表を作っていたら、10月4日から22日まで3日おきに失敗が続いたことになるところでした。
出力ファイルには残らない失敗がある
45件で数え終わりにしなかったのは、cronのセッション側にも記録があるからです。Hermesは ~/.hermes/state.db に、セッションごとの終了理由、API呼び出し数、入力トークン数を持っています。公式に形が保証された出力ではないので、読み取り専用で開きます。
条件は「cronのセッションで、API呼び出しが0回、入力トークンも0」にしました。
import sqlite3
from datetime import datetime
from pathlib import Path
db = Path.home() / ".hermes" / "state.db"
con = sqlite3.connect(f"file:{db}?mode=ro", uri=True)
start = datetime(2026, 9, 20).timestamp()
sql = """
select date(started_at, 'unixepoch', 'localtime') as day,
model, end_reason, count(*)
from sessions
where source = 'cron'
and started_at >= ?
and api_call_count = 0
and input_tokens = 0
group by day, model, end_reason
order by day
"""
total = 0
for day, model, reason, n in con.execute(sql, (start,)):
total += n
print(day, model, reason, n)
print("total", total)
このスクリプトを zero_calls.py に保存して python3 zero_calls.py で実行した出力です。
2026-09-21 grok-4.6 cron_incomplete_no_output 5
2026-09-22 grok-4.6 cron_incomplete_no_output 12
2026-09-23 grok-4.6 cron_incomplete_no_output 4
2026-09-24 grok-4.6 cron_incomplete_no_output 3
2026-09-25 grok-4.6 cron_incomplete_no_output 6
2026-09-28 grok-4.7 cron_incomplete_no_output 1
2026-09-30 grok-4.7 cron_complete 8
2026-10-01 grok-4.7 cron_complete 9
2026-10-02 grok-4.7 cron_complete 2
2026-10-05 claude-sonnet-5-5 cron_complete 1
total 51
51件です。波が2つあります。
| 期間 | 件数 | 終了理由 |
|---|---|---|
| 9月21日から28日 | 31 | cron_incomplete_no_output |
| 9月30日から10月2日 | 19 | cron_complete |
前半は「出力なしで未完」と記録されます。ここは見れば異常だと分かります。後半は同じ0回なのに、終了理由が「完了」です。10月1日分の出力ファイルを開くと、## Error の下に403が書かれています。完了の印が付いたまま、中身は失敗でした。
9月26日から29日の4日間は、28日の1件を除いてGrokが動いていて、30日にまた止まっています。切れて、戻って、また切れています。利用枠の側で何が起きたのかは、私の手元の記録からは分かりません。
数が合わない6件を、そのままにしておく
セッションの51件と、エラーファイルの45件は一致しません。差の6件の中身は次のとおりです。
- Grokのセッションで、エラーファイルが残っていないもの: 5件(9月21日に3件、9月28日と30日に1件ずつ)
- 10月5日のClaudeセッション: 1件
前者の5件は、API呼び出しが0回という点では同じです。ただ、403だったと言える文字列がありません。同じ時期、同じモデルなので利用枠切れだったと考えるのが自然です。私はそう読みますが、確認できたのは45件だけです。後者は原因を調べていないので、ここでは扱いません。
10月3日以降と、残っている不安
10月3日から24日の22日間で、cronのセッションは367件です。主なモデル別では、gpt-6-solが179件、claude-sonnet-5-5が150件、grok-4.7は13件でした。13件は、API呼び出しが1回以上ありました。API呼び出しが0回のセッションは、先ほどのClaudeの1件だけです。
ジョブごとに別プロバイダへ固定していった結果、API呼び出し0回の日はほぼ消えました。
不安は残っています。hermes config get cron.model は今も grok-4.7、プロバイダは xai-oauth を返します。有効なジョブ32本のうち、個別に固定していないのは9本、Grokに固定したままなのが4本です。この13本は、Grokの利用枠がまた切れれば同じ失敗を繰り返します。
そのとき「完了」を信じて朝の確認を省くと、止まったことに気づくのが遅れます。今回の2つ目の波は、終了理由だけを見ていては気づけない形でした。
朝に見る場所を変える
結局、終了理由の列は信用の根拠に使えませんでした。見るのは次の2つです。
- API呼び出しが0回で、入力トークンも0のcronセッションが、前日に1件でもあるか
- あれば、そのジョブの出力ファイルの
## Errorに何が書かれているか
どちらも、上のスクリプトとgrepを手で実行して確かめた方法です。毎日自動で回す仕組みにまでは、まだしていません。
