Claude Code remote-controlがVolta環境で動かないバグを直した
claude remote-control を実行した瞬間、こう返ってくる。
[08:27:41] Session failed: C:\Users\numbe\AppData\Local\Volta\tools\image\node\22.22.0\node.exe: bad option: --sdk-url session_0196WpHd1WRMRYLxSG7wTBNc
remote-controlはClaude Code 2.1.xで追加された機能で、claude.aiのウェブUIからローカルのClaude Codeセッションを操作できる。しかしVolta環境だとセッション確立直後に死ぬ。
環境
- Windows 11
- Node.js v22.22.0(Volta管理)
- Claude Code 2.1.52(
volta install @anthropic-ai/claude-codeでインストール)
原因
Claude Codeの remote-control は、リモートセッションが来ると子プロセスを child_process.spawn で起動する。そのコードがこうなっている:
// cli.js 内の IPq 関数(minified)
let O = iHz(A.execPath, $, { cwd: K, stdio: [...], env: H, windowsHide: true });
ここで:
iHz=child_process.spawnA.execPath=process.execPath=node.exeのパス$=["--print", "--sdk-url", <url>, "--session-id", <id>, ...]
つまり実行されるコマンドは:
node.exe --print --sdk-url wss://... --session-id session_xxx ...
node.exe に直接 --print や --sdk-url を渡しているので、Node.jsがこれを自身のオプションと解釈して bad option で落ちる。
正しくは node.exe cli.js --print --sdk-url ... のように、引数の先頭に cli.js のパスが必要。
なぜ公式ビルドで動くのか(推測)
公式のスタンドアロンインストーラーでは、process.execPath がNode.jsバイナリではなくClaude Code自体のバイナリを指す可能性がある(SEA: Single Executable Applicationなど)。npm/Volta経由のインストールでは process.execPath は常に node.exe なので、cli.js パスを別途渡す必要がある。
修正手順
1. 正しいcli.jsの場所を特定する
Volta環境では cli.js が複数箇所に存在する。実際にロードされるのは packages ディレクトリ のほう。
# これは使われない(npm install -g 用)
Volta/tools/image/node/22.22.0/node_modules/@anthropic-ai/claude-code/cli.js
# これが本物(volta install 用)
Volta/tools/image/packages/@anthropic-ai/claude-code/node_modules/@anthropic-ai/claude-code/cli.js
フルパス:
C:\Users\numbe\AppData\Local\Volta\tools\image\packages\@anthropic-ai\claude-code\node_modules\@anthropic-ai\claude-code\cli.js
正しいファイルかどうかは、ファイル先頭に console.error("LOADED") を入れて claude --version を実行すれば確認できる。
2. spawn呼び出しを修正する
cli.js の中で以下の文字列を検索する:
iHz(A.execPath,$,{cwd:K,stdio:["pipe","pipe","pipe"],env:H,windowsHide:!0})
これを以下に置換:
iHz(A.execPath,[process.argv[1],...$],{cwd:K,stdio:["pipe","pipe","pipe"],env:H,windowsHide:!0})
process.argv[1] は cli.js のフルパスを指すので、子プロセスが node.exe cli.js --print --sdk-url ... として正しく起動される。
3. 動作確認
claude remote-control
以下のように表示されれば成功:
·✔︎· Connected · project-name · master
Continue coding in the Claude app or https://claude.ai/code/session_xxx
space to show QR code
ワンライナーパッチ(コピペ用)
Pythonで置換する場合:
import re
path = r"C:\Users\numbe\AppData\Local\Volta\tools\image\packages\@anthropic-ai\claude-code\node_modules\@anthropic-ai\claude-code\cli.js"
with open(path, "r", encoding="utf-8") as f:
content = f.read()
old = 'iHz(A.execPath,$,{cwd:K,stdio:["pipe","pipe","pipe"],env:H,windowsHide:!0})'
new = 'iHz(A.execPath,[process.argv[1],...$],{cwd:K,stdio:["pipe","pipe","pipe"],env:H,windowsHide:!0})'
if old in content:
content = content.replace(old, new, 1)
with open(path, "w", encoding="utf-8") as f:
f.write(content)
print("Patched successfully")
else:
print("Target string not found - already patched or different version")
アップデート後の再パッチ
volta install @anthropic-ai/claude-code でアップデートするとパッチが消える。その場合は同じ手順で再度パッチを当てる。
将来のバージョンでは修正されている可能性もあるので、まずパッチなしで claude remote-control を試してから判断すればよい。
調査で嵌ったポイント
最初の1時間は 間違ったcli.jsを編集していた。
Voltaは volta install で入れたパッケージと npm install -g で入れたパッケージを別の場所に格納する:
| インストール方法 | 格納先 |
|---|---|
volta install | Volta/tools/image/packages/<pkg>/node_modules/ |
npm install -g | Volta/tools/image/node/<version>/node_modules/ |
which claude や npm list -g が返すパスは後者を指すが、PowerShellで claude を叩いたときにVoltaシムが解決するのは前者。デバッグ出力を入れても何も出ない、パッチが効かない、と悩んだ末にようやく find で全コピーを洗い出して気づいた。
GitHub Issue
この問題はClaude Code本体のバグ。npm/Volta経由でインストールした場合、process.execPath が node.exe を指すのに process.argv[1](スクリプトパス)がspawn引数に含まれていない。
報告先: https://github.com/anthropics/claude-code/issues
再現条件:
- Windows + Volta(おそらくnvm-windowsやfnmでも同様)
volta install @anthropic-ai/claude-codeでインストールclaude remote-controlを実行