ClaudeCodeOptionsのcwdが反映されない理由と回避策 — Python Agent SDK
claude-agent-sdk-pythonのissueが報告し続けるcwd無視の症状を公式ドキュメントの仕様から読み解き、回避策と症状別の見分け方を示します。
Python版Agent SDKでcwdを指定しても、Claudeが別のディレクトリでファイル操作をすることがあります。claude-agent-sdk-pythonのissueには2025年6月から同じ症状の報告が続き、2026年4月のコメントまで解消していません。原因はcwd自体が効いていないことではなく、system_promptを未指定のまま使うと作業ディレクトリの情報がClaudeに渡らないことです。claude_codeプリセットを指定すれば、この情報が補われます。
cwdが反映されないという報告
claude-agent-sdk-python のリポジトリには、cwd オプションを設定してもClaudeが指定したディレクトリで動いていないように見える、という報告を集めたissueがあります。タイトルはそのまま cwd in ClaudeCodeOptions does not seem to be honored at all です。2025年6月15日に最初の報告が投稿されてから2026年4月5日の直近コメントまで、10件のコメントが約10か月にわたって書き込まれ続けており、Anthropic側からの返信はこのスレッドには見当たりません。
最初の報告者は次のコードを添えています。cwd に絶対パスを渡し、bypassPermissions まで指定しているのに、無関係なパスでファイル操作が行われるという内容です。
options = ClaudeCodeOptions(
cwd=Path("/path/to/app"),
system_prompt="You are a helpful assistant",
allowed_tools=[...],
permission_mode="bypassPermissions",
continue_conversation=True,
max_turns=10,
)以降のコメントでは症状の書かれ方が少しずつ違います。elmspace は「cwd の指定に従わないだけでなく、他のディレクトリでタスクを実行しようとする。権限エラーで止まってくれたから助かった」と、意図しないディレクトリへの書き込みが実際に走りかけたことを報告しています。marswong は2025年10月の時点でもなお /tmp へのランダムな書き込みが続いていると報告しています。2026年1月には widarlein が同じ症状を報告しつつ、システムプロンプトへの追記はまだ試していないと書き添え、翌2026年2月には qqgiovani がWindows環境でも同じ問題が起きていると報告しました。2026年3月の oubeichen のコメントは「問題は依然として存在する」という一文だけで、9か月以上前の最初の報告から状況が変わっていないことを示しています。jellehelsen は2026年3月にgitのworktree上で実行したところ、worktree側ではなくメインリポジトリのファイルが更新されたという、ファイル操作の安全性に関わる報告を寄せました。1本のissueに複数の異なる原因が混ざっている可能性がある状態です。
cwdの定義とClaudeAgentOptionsへの改名
cwd はSDKの設定オブジェクトで「Claude Codeを起動するサブプロセスの作業ディレクトリ」を指定するフィールドです。現行のリファレンスでは ClaudeAgentOptions の1フィールドとして cwd: str | Path | None = None と定義され、説明文は「Current working directory」の一文だけです。
issueのタイトルにある ClaudeCodeOptions は、このクラスの旧名です。移行ガイドによると、Claude Agent SDK v0.1.0でパッケージ名が claude-code-sdk から claude-agent-sdk に変わったのと同時に、Pythonの型名も ClaudeCodeOptions から ClaudeAgentOptions に改名されました。挙動は変わらず名前だけの変更です。この改名と合わせて、system_prompt を指定しなかったときの既定動作も変わっています。
v0.0.x(claude-code-sdk) | v0.1.0以降(claude-agent-sdk) | |
|---|---|---|
| Pythonのクラス名 | v0.0.x(claude-code-sdk)ClaudeCodeOptions | v0.1.0以降(claude-agent-sdk)ClaudeAgentOptions |
system_prompt未指定時の既定 | v0.0.x(claude-code-sdk)Claude Codeのシステムプロンプト | v0.1.0以降(claude-agent-sdk)最小限のシステムプロンプト |
移行ガイドでは、これらの破壊的変更をまとめて「分離と明示的な設定を高めるため」の変更だと説明されています。エージェントの挙動をClaude Codeの既定に暗黙で依存させず、必要な要素は呼び出し側が明示的に指定する設計へ寄せた変更、という位置づけです。型名だけを見ると単なるリネームですが、実際に挙動へ効いてくるのはシステムプロンプトの既定変更の方です。少なくとも最初の報告では、混乱の一因がここにあります。改名の経緯やパッケージ名の変更点はAgent SDK移行ガイドにまとめています。
なぜcwdを設定してもClaudeが気づかないのか
system_promptの設定方法によって、渡る情報の範囲は変わります。設定しない場合はツール呼び出しの最低限の指示だけを含む最小プロンプトが使われ、セキュリティ・安全性の指示や作業ディレクトリの環境情報は含まれません。独自の文字列をそのまま渡す場合も同様で、4つの設定方法を比較した表では、カスタムプロンプトのEnvironment contextの列が「Must be provided」、つまり環境情報を呼び出し側が自分で用意する必要がある側に分類されています。
最初の報告者のコードは system_prompt="You are a helpful assistant" と文字列を直接渡していました。ドキュメントの定義に照らすと、この設定では作業ディレクトリを含む環境コンテキストがClaudeに渡っていません。つまり cwd はサブプロセスの起動場所としては渡っていても、Claude自身がそれを認識する経路は呼び出し側が別に用意する必要があります。
他の報告者のコードは公開されていないため同じ設定だったかは確認できませんが、最初の報告についてはこの条件に正確に一致します。
回避策 — claude_codeプリセットを指定する
最初の報告の条件に対しては、シンプルな回避策があります。system_prompt に独自の文字列を渡す代わりに、claude_code プリセットを指定する方法です。
from claude_agent_sdk import query, ClaudeAgentOptions
options = ClaudeAgentOptions(
cwd="/path/to/app",
system_prompt={"type": "preset", "preset": "claude_code"},
permission_mode="bypassPermissions",
)このissueの最後のコメント(2026年4月)でも、投稿者はプリセットを指定する方法で問題が解決したと報告しています。独自の指示を足したい場合は append を使えば、プリセットの内容を残したまま末尾に追記できます。
options = ClaudeAgentOptions(
cwd="/path/to/app",
system_prompt={
"type": "preset",
"preset": "claude_code",
"append": "Always run tests before finishing.",
},
)プリセットと append・カスタムプロンプトの使い分けはAgent SDKのシステムプロンプトをカスタマイズする4つのアプローチで比較しています。複数のディレクトリで大量にセッションを動かしていてプロンプトキャッシュのヒット率が気になる場合は、同記事の exclude_dynamic_sections の節も合わせて確認してください。
cwdが実際に効いているかを検証する方法
プリセットを指定しても改善したかどうかを目視だけで判断するのは難しいため、Claude自身に作業ディレクトリを言わせて確認する方法があります。allowed_tools に Bash を含め、最初のプロンプトで pwd を実行させて結果を返させるだけです。
async for message in query(
prompt="Run `pwd` and report the exact output, then stop.",
options=ClaudeAgentOptions(
cwd="/path/to/app",
system_prompt={"type": "preset", "preset": "claude_code"},
allowed_tools=["Bash"],
permission_mode="bypassPermissions",
),
):
print(message)返ってきた pwd の出力が指定した cwd と一致していれば、サブプロセス自体は正しいディレクトリで起動できています。一致しない場合は、システムプロンプトの設定を見直す前に、まず cwd の値そのもの(相対パスのままになっていないか、シンボリックリンクを経由していないか)を疑う方が近道です。ただしこのpwd確認はサブプロセスの起動場所を見るだけなので、Claudeが絶対パスを使って別ディレクトリへ書き込む症状までは検出できない点に注意してください。
症状ごとの見分け方
issueに書き込まれた報告は、原因がおそらく同じもの・確認が要るものに分かれます。
| 症状 | 読み方 |
|---|---|
出力パスの先頭に/privateが付くだけ(/tmp/app → /private/tmp/app) | 読み方macOSのシンボリックリンク解決によるもので、cwdのバグではないと投稿者自身が後日訂正しています |
system_promptに独自の文字列を渡している | 読み方環境コンテキストが失われる既知の条件と一致。claude_codeプリセットへの切り替えで解消する可能性が高い |
| Windows環境で発生 | 読み方このケースについて個別の説明は見当たりません。プリセット指定を試したうえで再現するか切り分けます |
| gitのworktreeで動かすとメインリポジトリ側が更新される | 読み方cwdに渡した値が実際にworktreeの絶対パスへ解決されているか(相対パスのままになっていないか)を確認します |
macOSの症状は、報告者本人が後日「Macのシンボリックリンクの挙動によるもので、自分の問題は別だった」と訂正している点が手がかりになります。/tmp は多くのmacOS環境で /private/tmp へのシンボリックリンクになっており、パス解決の過程で先頭に /private が付くのはcwdの設定ミスではなく表示上の差分にすぎません。gitのworktreeのケースは、cwdに渡した値が実際にworktreeの絶対パスへ解決されているかがまず確認点です。相対パスのまま渡すと、プロセス起動時のカレントディレクトリ(多くの場合メインリポジトリ側)を基準に解決され、worktree側ではなく元のリポジトリのファイルが更新されることがあります。Windows環境の報告については、該当のプラットフォーム差についての記述が見当たらず、他の報告と同じ原因なのか別の原因なのかは判別できていません。
Pythonの claude-agent-sdk にはファイル書き込み範囲を制限する filesystem サンドボックス設定が無く(TypeScript版のみ)、can_use_tool コールバックで書き込み先を検査する方法が代わりの防御線になります。書き込み対象のパスが cwd 配下かどうかを判定し、外れていれば拒否する形です。
from pathlib import Path
from claude_agent_sdk import ClaudeAgentOptions
from claude_agent_sdk.types import (
PermissionResultAllow,
PermissionResultDeny,
ToolPermissionContext,
)
BASE_DIR = Path("/path/to/app").resolve()
async def restrict_to_cwd(
tool_name: str, input_data: dict, context: ToolPermissionContext
) -> PermissionResultAllow | PermissionResultDeny:
if tool_name in ("Write", "Edit"):
target = Path(input_data.get("file_path", "")).resolve()
if not target.is_relative_to(BASE_DIR):
return PermissionResultDeny(
message=f"{target} is outside {BASE_DIR}", interrupt=True
)
return PermissionResultAllow(updated_input=input_data)
options = ClaudeAgentOptions(cwd=BASE_DIR, can_use_tool=restrict_to_cwd)elmspaceが報告した「無関係なディレクトリでの操作」は権限エラーで止まりましたが、これは実行環境がたまたま権限を拒否しただけです。can_use_toolでパスを明示的に検査しておけば、権限設定に関わらず書き込み範囲をcwd配下に固定できます。サンドボックス設定の詳細はAgent SDKのsandbox設定にまとめています。CLIが起動できない・プロセスが途中で終了するといった別種のエラーはAgent SDKのエラー集を参照してください。
issueがopenのままでも、プリセット指定で症状を切り分けられる
issueはクローズされておらず、Anthropic側の担当者からの返信も付いていません。一方で、claude_code プリセットを指定すればこの症状が解消することは、比較表(Custom systemPromptではEnvironment contextを呼び出し側が用意する必要がある)と、実際に解決したと報告したコメントの両方から確認できます。
issue自体がクローズされる見込みが立っていない以上、読者側でできることは、system_promptをclaude_codeプリセットに揃え、pwd確認やcan_use_toolでの書き込み先チェックを組み合わせて挙動を実際に確認することです。
まとめ
ClaudeCodeOptions(現行のClaudeAgentOptions)の cwd は、Claude Codeのサブプロセスをどこで起動するかを指定するオプションです。作業ディレクトリを含む環境コンテキストをClaudeに渡すのは system_prompt の claude_code プリセットの役割で、独自の文字列に置き換えるとその情報は呼び出し側が用意しない限り渡りません。cwd が効いていないように見える場合は、まず system_prompt に claude_code プリセットを指定して切り分け、can_use_tool での書き込み先チェックも併用してください。macOSのシンボリックリンク表示やWindows・git worktreeでの個別報告は表の見分け方を参考に、再現条件を先に絞り込んでから対策を検討してください。