何が起きたか
Claude Code の hook 機能を使って、セッション開始時に Python スクリプトを自動起動しようとしたときの話。settings.json に PreToolUse フックを書いて python3 myscript.py を呼んだら、コマンド自体は実行されているのにスクリプトが黙って落ちる。ログにも何も残らない。エラーも出ない。「あれ、動いてる?」状態が30分続いた。原因はパス・環境変数・作業ディレクトリの三重奏だった。
環境
- macOS Sequoia 15.x / MacBook Pro M5 32GB
- Claude Code(最新ビルド)
- Python 3.12(Homebrew 管理)
- 仮想環境:
~/Documents/AI_Automation_Base/.venv - 対象スクリプト:
01_Scripts/monitoring/session_start_hook.py(セッション開始時に Slack 通知 + ステータスチェックを走らせるやつ) - Ollama:
localhost:11434で稼働中
詰まったポイント
フックの書き方は公式ドキュメント通りに書いた。settings.json に以下を追加:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 ~/Documents/AI_Automation_Base/01_Scripts/monitoring/session_start_hook.py"
}
]
}
]
}
}
実行ログを見ると exit code: 0 で正常終了している。なのにスクリプトの出力が一切ない。print() すら出ない。
最初に疑ったのは「スクリプト自体が壊れてるか」だったが、ターミナルで直接 python3 01_Scripts/monitoring/session_start_hook.py を叩くと普通に動く。ここで「フック経由だと何かが違う」と気づいた。
次のエラー(推測ではなく実際に 2>&1 でキャプチャして確認した内容):
/usr/bin/python3: No such file or directory
python3 の解決先が /usr/local/bin/python3 や /opt/homebrew/bin/python3 ではなく、フック実行時の PATH が削ぎ落とされた状態で /usr/bin/python3 を探しに行っていた。macOS の /usr/bin/python3 は Xcode Command Line Tools が入っていないと存在しない。
解決までの手順
ステップ1: フックの出力をまずキャプチャする
2>&1 をつけてエラーを拾えるようにする。出力先を一時ファイルに書き出した。
{
"command": "python3 ~/Documents/AI_Automation_Base/01_Scripts/monitoring/session_start_hook.py >> /tmp/hook_debug.log 2>&1"
}
/tmp/hook_debug.log を見ると No such file or directory が出ていた。ここでようやく原因が「パス問題」と確定した。
“
ステップ2: フルパスで python3 を指定する
which python3 で確認してフルパスを直書きする。
which python3
# → /opt/homebrew/bin/python3
{
"command": "/opt/homebrew/bin/python3 ~/Documents/AI_Automation_Base/01_Scripts/monitoring/session_start_hook.py >> /tmp/hook_debug.log 2>&1"
}
これで No such file or directory は消えた。が、次のエラーが来た。
ステップ3: 仮想環境の依存パッケージ問題
スクリプトが httpx をインポートしているが、システムの Python3 にはない。仮想環境に入れてあるやつを使わないといけない。
解決策は2つある。フックから bash -c 経由でシェルスクリプトを呼び、その中で source .venv/bin/activate してから Python を起動する方法と、仮想環境の Python バイナリを直指定する方法。後者のほうが単純なので採用した。
{
"command": "/Users/ichinosetaito/Documents/AI_Automation_Base/.venv/bin/python3 /Users/ichinosetaito/Documents/AI_Automation_Base/01_Scripts/monitoring/session_start_hook.py >> /tmp/hook_debug.log 2>&1"
}
ステップ4: 作業ディレクトリの問題
スクリプト内で open("04_Config/.env") のような相対パスを使っていた部分が FileNotFoundError になった。フック実行時のカレントディレクトリは Claude Code がどこで起動したかによって変わる。絶対パスに直すか、スクリプト先頭で os.chdir() するかで対処。
import os
os.chdir(os.path.dirname(os.path.abspath(__file__)))
# これで __file__ の場所を基準に相対パスが使える
ステップ5: ANTHROPIC_API_KEY の漏れ問題
これが一番ハマった。フックから subprocess.run(["claude", "-p", ...]) を呼んでいる箇所があり、フック実行時の環境変数に ANTHROPIC_API_KEY が含まれていると API クレジットを消費しようとして残高不足エラーになる。Claude Code のサブスクリプション利用なのに API クレジットを食いに行く謎挙動。
# NG: 環境変数をそのまま渡す
subprocess.run(["claude", "-p", prompt])
# OK: ANTHROPIC_API_KEY を除外して渡す
env = {k: v for k, v in os.environ.items() if k != "ANTHROPIC_API_KEY"}
subprocess.run(["claude", "-p", prompt], env=env)
コード/設定の抜粋
最終的な settings.json のフック設定:
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "/Users/ichinosetaito/Documents/AI_Automation_Base/.venv/bin/python3 /Users/ichinosetaito/Documents/AI_Automation_Base/01_Scripts/monitoring/session_start_hook.py >> /tmp/claude_hook.log 2>&1"
}
]
}
]
}
}
スクリプト冒頭の定形処理:
import os
import sys
# 作業ディレクトリをスクリプトの場所に固定
os.chdir(os.path.dirname(os.path.abspath(__file__)))
# claude -p を使う場合は API キーを除外
def run_claude(prompt: str) -> str:
env = {k: v for k, v in os.environ.items() if k != "ANTHROPIC_API_KEY"}
result = subprocess.run(
["claude", "-p", prompt],
capture_output=True, text=True, env=env
)
return result.stdout.strip()
デバッグ用のシェル:
#!/bin/bash
# フックと同じ条件でスクリプトを手動実行してテストする
env -i HOME="$HOME" PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin" \
/Users/ichinosetaito/Documents/AI_Automation_Base/.venv/bin/python3 \
/Users/ichinosetaito/Documents/AI_Automation_Base/01_Scripts/monitoring/session_start_hook.py
env -i で環境変数をクリーンにした状態を再現できるので、「ターミナルでは動くのにフックで動かない」を手元で再現しやすくなる。
試してわかったこと
Claude Code のフックが実行されるプロセスは、ターミナルのシェルとは別の環境で動いている。PATH が削ぎ落とされているし、VIRTUAL_ENV も当然セットされていない。「ターミナルで動く = フックで動く」とはならない。
フックで何かを動かすときのチェックリストをまとめると:
python3はフルパスか.venv/bin/python3直指定- スクリプト内の相対パスは
os.chdir()かpathlib.Path(__file__)基準で管理 claude -p呼び出しがあるならANTHROPIC_API_KEYを env から抜く- まず
>> /tmp/debug.log 2>&1でエラーをキャプチャしてから直す
フックはサイレントに失敗する。exit code: 0 が返ってきても「何もしなかった」だけで成功している可能性がある。ログ出力は必須。
まとめ
Claude Code hook の Python 起動は「PATH・仮想環境・作業ディレクトリ」の三つ全部を意識しないと黙って失敗する。デバッグは 2>&1 でログをキャプチャしてから始める。claude -p を呼ぶスクリプトは ANTHROPIC_API_KEY の除外を忘れずに。