【Claude Code】
なぜかAIエージェントからは
pythonコマンドが動かない?

  • Claude CodeにPython製の日報コマンドを実行させたら、Terminalでは動くのにModuleNotFoundErrorで落ちた。
  • 原因は、同じMacの中にpython3を名乗るプログラムが2つ住んでいて、呼び出し元によって答える方が違っていたこと。
  • macOSのTerminalは起動時にPATHを組み立て直すが、AIエージェントのセッションはその手順を通らないことがある。
  • その場しのぎのpip installに加えて、コマンド自身に失敗理由を語らせる仕込みをした。

関連記事

1. python3と打つたびに、同じPythonが返ってきているとは限らない

最近、日々の作業ログをAIエージェントに読ませて、日報の形にまとめてもらっています。
使っているのはActivityWatchというツールと、それを日ごとに集計する自作のコマンドawlogです。

1. python3と打つたびに、同じPythonが返ってきているとは限らない

昨日も同じようにawlog d 1と打たせたところ、いつもは何も言わずに1日分のログを吐き出すコマンドが、突然こう言い出しました。

ModuleNotFoundError: No module named 'aw_client'

自分のTerminalで打てば、何の問題もなく動きます。
同じMac、同じコマンド、同じファイルです。
違うのは、それを実行しているのが自分の手ではなく、Claude Codeというコーディングエージェントだったことだけでした。

では、何が違ったのか。
疑ってかかるべきはpython3という名前そのものでした。
python3という文字列は、特定の実体を指す固有名詞ではありません。
シェルがPATHという変数に並んだディレクトリを順番に探し、最初に見つかったpython3という名前の実行ファイルを使う、という約束事があるだけです1。
python3は役職名のようなもので、その場に座っているのが誰かはPATH次第だったわけです。

2. PATHの並び順が、答える相手を決めている

実際に調べてみると、このMacにはpython3と名乗るプログラムが少なくとも2つありました。
一つは/usr/bin/python3、もう一つは/usr/local/bin/python3です。
which -a python3で両方の居場所を並べてみて、初めてその存在に気づきました。

2.1. CommandLineToolsのpython3と、python.org版のpython3

/usr/bin/python3の正体は、AppleがXcodeと一緒に配っているCommand Line Toolsに同梱されたPythonでした。
実体は/Library/Developer/CommandLineTools/usr/bin/python3にあり、バージョンは3.9系です2。
macOSを開発機として使うなら最低限入っている、いわば標準装備のPythonという位置づけです。

もう一つの/usr/local/bin/python3は、python.orgが配布しているインストーラーから入れた3.12系でした3。
aw_clientというモジュールは、以前こちらの3.12系にだけpip installしていたものです。
モジュールが存在しなかったのではなく、探しに行った先が違っていた、というのが真相でした。

2.2. なぜAIエージェントのセッションだけ順番が違ったのか

2.2. なぜAIエージェントのセッションだけ順番が違ったのか

普段Terminalを開くと、macOSはpath_helperという仕組みでPATHを組み立て直し、/usr/local/binを/usr/binより先に並べます4。
だから何も意識しなくてもpython3と打てば3.12系が呼ばれ、aw_clientが見つかっていました。

ところがClaude Codeのセッションは、ログインシェルとしてTerminalを開く手順をそのまま踏んでいるわけではありません5。
実際にそのセッションのPATHを確認すると、/usr/binが/usr/local/binより先に並んでいて、順番が逆転していました。
同じユーザーの同じMacでも、起動のされ方が違えば、PATHという名簿の並び順まで変わってしまうということです。

3. その場しのぎと、恒久策を両方打つ

直った理由も、順を追うと単純です。
まずはAIエージェントのセッションから見えている3.9系のpython3に対してpip install --user aw-clientを打ち、その場をしのぎました6。
ただこれだけで終わらせると、次に別の環境からこのコマンドを呼んだときにまた同じ現象が起きます。

3. その場しのぎと、恒久策を両方打つ

結果として、このMacにはaw_clientが3.9系と3.12系の両方に別々にインストールされている状態になりました。
実害はありませんが、同じ道具を違う戸棚に二重にしまったような座りの悪さは残ります。

3.1. awlogにコメントと、エラー時のヒントを仕込む

恒久的な対策として、awlogというスクリプト自身に手を入れました。
まずスクリプトの説明コメントに、python3の解決先が実行環境によって変わりうる旨を書き添えました。
コメントはエラーが起きて中身を読みにいった人にしか届きませんが、それでも次に同じ場所で詰まる人への案内にはなります。

もう一つは、エラーメッセージそのものへの仕込みです。
awlogの内部でPythonを呼ぶ処理をラップし、出力にModuleNotFoundErrorという文字列が含まれていれば、追加でこう表示するようにしました7。

[awlog] python3 に必要なモジュールが見つかりません。
通常のmacOS Terminalと、Claude CodeなどAIエージェントのセッションとで
別の python3(別のモジュール環境)が使われている可能性があります。
実際に使われている python3 を確認し、そちらにモジュールを入れてください:
  python3 -c 'import sys; print(sys.executable)'
  python3 -m pip install --user aw-clientCode language: JavaScript (javascript)

これで、次に同じ現象が起きたときは、原因を1から調べ直さずに済みます。
コマンドが自分の失敗理由を語ってくれるのは、地味に助かる仕掛けです。

python3という一つの名前の裏に、複数の実体が住んでいる。
考えてみれば当たり前のことですが、Terminalだけを使っている間は意識する機会がありませんでした。
AIエージェントという、自分とは違う起動のされ方をする「もう一人の自分」がPCを操作するようになって初めて、普段は隠れていたPATHの並び順が姿を現した、という感じです。

  1. シェルはPATH環境変数に列挙されたディレクトリを順に探索し、最初に見つかった実行ファイルを実行するという仕組みで動いている。 – Where PATHs come from
  2. Xcode Command Line Toolsをインストールすると、Apple製のPython 3.9.6が付属し、Core SymbolicationやLLDBなど一部コンポーネントが依存する形で同梱されている。 – Why is Python included in Xcode Command Line Tools?
  3. python.orgのmacOS用インストーラーは/Library/Frameworks/Python.frameworkにPython本体を配置し、そこから/usr/local/binへpython3などのシンボリックリンクを張る構成になっている。 – 5. Using Python on macOS — Python 3.14.7 documentation
  4. path_helperは/etc/pathsと/etc/paths.d以下のファイルを読み込んでPATH環境変数を組み立て直すユーティリティで、/etc/pathsの末尾には/usr/local/binが記載されている。 – Mastering the path_helper utility of MacOSX
  5. path_helperはログインシェル起動時に読み込まれる/etc/zprofileの中で呼ばれる仕組みで、非ログイン・非対話のシェルではこの処理自体が実行されないため、PATHの並び順が変わる。 – All Patterns and Solutions for PATH Issues in zsh on macOS
  6. aw-clientはActivityWatchのREST APIをラップしたPython向けクライアントライブラリで、pip install aw-clientで導入できると公式READMEに記載されている。 – GitHub – ActivityWatch/aw-client
  7. ModuleNotFoundErrorは、Pythonのインポートシステムが指定されたモジュールを見つけられなかった場合に送出される例外として、公式リファレンスに定義されている。 – 5. The import system — Python 3.14.7 documentation