サンドボックス化されたエージェントがブラウザCDPに接続できない — ensureSandboxBrowserで127.0.0.1がハードコードされている
サンドボックスモードが'all'に設定されていると、CDP websocket接続が失敗します。これは、ensureSandboxBrowserがCDPポートをホスト127.0.0.1にマッピングするためで、Dockerでサンドボックス化されたエージェントコンテナ内からはアクセスできません。
🔍 症状
主なエラーの発生状況
エージェントが agents.defaults.sandbox.mode: "all" 設定のDockerコンテナ内で実行されている場合、browser ツールを呼び出すと接続エラーが発生します:
Error: Chrome CDP websocket for profile "openclaw" is not reachable after start.
at BrowserTool._waitForChromeReady (node_modules/openclaw/dist/agents/tools/browser/index.js:XXXX:XX)
at BrowserTool.open (node_modules/openclaw/dist/agents/tools/browser/index.js:XXXX:XX)
at ...
ネットワーク診断の結果
サンドボックス化されたエージェントコンテナ内から接続テストを行うと、問題の根本原因が明らかになります:
# Attempting to reach the CDP port via host loopback
$ curl -v http://127.0.0.1:9222
curl: (7) Failed to connect to 127.0.0.1 port 9222 after 0ms: Connection refused
# Confirming 127.0.0.1 is the container's own loopback
$ ip route show
default via 172.18.0.1 dev eth0
172.18.0.0/16 dev eth0 proto kernel scope link src 172.18.0.2
# The browser container exists on the Docker network but is unreachable via 127.0.0.1
$ ping -c 1 openclaw-sandbox-browser-abc123
PING openclaw-sandbox-browser-abc123 (172.18.0.3) 56(84) bytes of data.
64 bytes from openclaw-sandbox-browser-abc123 (172.18.0.3): icmp_seq=1 ttl=64 time=0.1 ms
問題を再現する設定
以下の最小限の設定で失敗が再現されます:
{
"agents": {
"defaults": {
"sandbox": {
"mode": "all",
"scope": "agent",
"workspaceAccess": "rw"
}
}
},
"tools": {
"sandbox": {
"tools": {
"alsoAllow": ["browser"]
}
}
}
}
ドキュメント化された回避策の失敗例
| 設定の試行内容 | 観察された結果 |
|---|---|
Default (no docker.network, network: none) | エージェントにネットワークがなく、CDPに到達不可 |
sandbox.docker.network: "openclaw-sandbox-browser" | それでも失敗—コードがコンテナDNSではなく 127.0.0.1 を使用するため |
sandbox.docker.extraHosts: ["host.docker.internal:host-gateway"] | 127.0.0.1 は依然としてコンテナループバックに解決される |
browser.profiles.remote-chrome.cdpUrl = "ws://127.0.0.1:9222" with attachOnly: true | "Browser attachOnly is enabled and profile 'remote-chrome' is not running." |
Same without attachOnly | "PortInUseError: Port 9222 is already in use." |
🧠 原因
アーキテクチャの概要
OpenClawのブラウザ自動化アーキテクチャには、2つの異なるランタイムコンテキストが関与しています:
- ブラウザサイドカーコンテナ: CDPが有効なChromiumを実行するDockerコンテナで、`openclaw-sandbox-browser` Dockerネットワークに接続されています。
- エージェントサンドボックスコンテナ: エージェントコードを実行するオプションのDockerコンテナで、ブラウザコンテナと同じ `openclaw-sandbox-browser` ネットワークに接続される可能性があります。
欠陥のあるポートマッピング戦略
src/agents/sandbox/browser.ts の ensureSandboxBrowser 関数は、ブラウザコンテナを作成し、CDPポートを以下のパターンで公開します:
// Simplified representation of the problematic code path
async function ensureSandboxBrowser(config) {
const browserContainer = await docker.createContainer({
// ... container config ...
HostConfig: {
PortBindings: {
"9222/tcp": [{ HostIp: "127.0.0.1", HostPort: cdpPort }] // ← THE BUG
}
}
});
// Later, browser tool client dials this hardcoded address:
const cdpUrl = `ws://127.0.0.1:${cdpPort}`;
return { container: browserContainer, cdpUrl };
}
コンテナ内で127.0.0.1が失敗する理由
ネットワーキングトポロジはクライアントの所在地によって異なります:
| クライアントの所在地 | 127.0.0.1 の解決先 | ブラウザコンテナに到達可能か |
|---|---|---|
| ホストマシン | ホストのループバックインターフェース | ○(ポートバインディング経由) |
| エージェントコンテナ | エージェントコンテナのループバック | ×(別のネットワーク名前空間) |
ブラウザコンテナのポートはホストのネットワーク名前空間上の 127.0.0.1 にバインドされています。エージェントコンテナは独自の分離されたループバックインターフェースを持つため、エージェントコンテナ内から 127.0.0.1 へのトラフィックはその名前空間から出ることはありません。
欠落しているロジック
以下の条件検出のための条件ロジックがコードに欠落しています:
- エージェントがDockerコンテナ内で実行されている
- エージェントコンテナがブラウザコンテナと同じDockerネットワークを共有している
- そのため、CDP URLはDockerの内部DNS解決を使用する必要がある
Dockerのユーザ定義ブリッジネットワークは、コンテナ名によるコンテナ間の自動DNS解決を提供します。エージェントコンテナが openclaw-sandbox-browser 上にあり、ブラウザコンテナ名が openclaw-sandbox-browser-abc123 である場合、エージェントは任意のポートでブラウザにこのDNS名で到達できます。
コードパスの分析
agent.sandbox.run() └── ensureSandboxBrowser() ├── Creates browser container on openclaw-sandbox-browser network ├── Binds port to 127.0.0.1 (host only) └── Returns cdpUrl = “ws://127.0.0.1:9222”
browser-tool.client.connect() └── Attempts to connect to cdpUrl └── If agent is containerized: connection refused (container loopback)
🛠️ 解決手順
前提条件となる設定
修正を適用する前に、エージェントサンドボックスがブラウザコンテナネットワークに参加するように設定されていることを確認してください。OpenClaw設定に以下を追加してください:
{
"agents": {
"defaults": {
"sandbox": {
"mode": "all",
"scope": "agent",
"workspaceAccess": "rw",
"docker": {
"network": "openclaw-sandbox-browser"
}
}
}
}
}
修正方法:コンテナDNSを使用するように ensureSandboxBrowser を変更する
src/agents/sandbox/browser.ts を見つけて、以下のパッチを適用してください:
修正前(問題のあるコード):
async function ensureSandboxBrowser(config) {
const containerName = generateContainerName('openclaw-sandbox-browser');
const container = await docker.createContainer({
name: containerName,
Image: config.browserImage,
HostConfig: {
NetworkMode: 'openclaw-sandbox-browser',
PortBindings: {
"9222/tcp": [{ HostIp: "127.0.0.1", HostPort: String(cdpPort) }]
}
}
});
const cdpUrl = `ws://127.0.0.1:${cdpPort}`;
return { container, cdpUrl, containerName };
}
修正後(修正済み):
async function ensureSandboxBrowser(config, agentContext) {
const containerName = generateContainerName('openclaw-sandbox-browser');
const cdpPort = config.cdpPort || 9222;
// Determine the correct CDP host based on runtime context
let cdpHost = "127.0.0.1";
if (agentContext?.isSandboxed && agentContext?.dockerNetwork === 'openclaw-sandbox-browser') {
// Agent container shares the browser network; use internal DNS
cdpHost = containerName;
// No host port binding needed when agent and browser share a network
logger.info(`Sandboxed agent detected; using internal DNS (${cdpHost}) for CDP`);
}
const createOptions = {
name: containerName,
Image: config.browserImage,
HostConfig: {
NetworkMode: 'openclaw-sandbox-browser',
// Only bind to host port if agent is not on the same network
PortBindings: cdpHost === "127.0.0.1"
? { "9222/tcp": [{ HostIp: "127.0.0.1", HostPort: String(cdpPort) }] }
: {} // Internal network access requires no host binding
}
};
const container = await docker.createContainer(createOptions);
const cdpUrl = `ws://${cdpHost}:${cdpPort}`;
return { container, cdpUrl, containerName };
}
代替方法:環境ベースの設定(コード変更なし)
ソースコードの変更が困難な場合は、CDP URLを直接設定してブラウザツールにコンテナの内部エンドポイントを使用させることもできます:
{
"browser": {
"profiles": {
"openclaw": {
"cdpUrl": "ws://openclaw-sandbox-browser-{{CONTAINER_ID}}:9222",
"launchOptions": {
"args": ["--disable-web-security"]
}
}
}
}
}
注意:これには設定時にブラウザコンテナ名を知る必要があります。コンテナ名は動的に生成されるため、一貫した命名プレフィックスを使用するか、docker ps --filter name=openclaw-sandbox-browser で確認してください。
Dockerネットワークの確認
OpenClawを起動する前に、ネットワークが存在することを確認してください:
# Create the shared network if it doesn't exist
$ docker network create openclaw-sandbox-browser 2>/dev/null || true
# Verify network configuration
$ docker network inspect openclaw-sandbox-browser --format '{{range .IPAM.Config}}Subnet: {{.Subnet}}{{end}}'
Subnet: 172.18.0.0/16
🧪 検証
手順1:ブラウザコンテナが正常に起動していることを確認する
$ docker ps -a --filter "name=openclaw-sandbox-browser" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
NAMES STATUS PORTS
openclaw-sandbox-browser-abc123 Up 2 minutes 0.0.0.0:9222->9222/tcp
手順2:エージェントコンテナが同じネットワーク上にあることを確認する
$ docker inspect openclaw-agent-xyz789 --format '{{range $k, $v := .NetworkSettings.Networks}}{{$k}}{{end}}'
openclaw-sandbox-browser
手順3:エージェントコンテナからCDP接続をテストする
$ docker exec openclaw-agent-xyz789 curl -s http://openclaw-sandbox-browser-abc123:9222/json/version | head -1
{
"Browser": "Chromium/120.0.6099.109",
"Protocol-Version": "1.3",
"User-Agent": "Mozilla/5.0 ...",
"V8-Version": "12.0.6099.109",
"WebKit-Version": "537.36 ..."
}
手順4:WebSocketアップグレードを確認する
$ docker exec openclaw-agent-xyz789 curl -v \
--no-buffer \
-H "Connection: Upgrade" \
-H "Upgrade: websocket" \
-H "Sec-WebSocket-Version: 13" \
-H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
http://openclaw-sandbox-browser-abc123:9222 \
2>&1 | grep -E "(HTTP|Upgrade: websocket)"
< HTTP/1.1 101 Switching Protocols
< Upgrade: websocket
< Connection: Upgrade
手順5:OpenClawで機能テストを実行する
CLIまたはAPIを通じてブラウザベースのタスクを実行します:
$ openclaw run --task "Navigate to example.com and return the page title"
[INFO] Sandbox mode: all
[INFO] Starting agent in container...
[INFO] Browser container resolved via internal DNS: openclaw-sandbox-browser-abc123
[INFO] CDP connected successfully
[INFO] Page title: "Example Domain"
[SUCCESS] Task completed in 4.2s
手順6:ホスト側からのアクセスも引き続き動作することを確認する
ホストマシン(コンテナ内ではない)から:
$ curl -s http://127.0.0.1:9222/json/version | head -1
{
"Browser": "Chromium/120.0.6099.109",
期待される結果:ホスト(127.0.0.1)と内部コンテナDNS(openclaw-sandbox-browser-*)の両方にアクセス可能であること。
⚠️ よくある落とし穴
1. ネットワークモードの不一致
エージェントサンドボックスは openclaw-sandbox-browser ネットワークに明示的に参加する必要があります。省略すると、エージェントは network: none(ネットワークなし)またはデフォルトのブリッジネットワークにデフォルト設定されます。
// WRONG: Missing network configuration
"sandbox": {
"mode": "all"
// Agent will have no network or wrong network
}
// CORRECT: Explicit network attachment
"sandbox": {
"mode": "all",
"docker": {
"network": "openclaw-sandbox-browser"
}
}
2. コンテナ起動時の競合状態
ブラウザコンテナがエージェントのCDP接続試行時に完全に準備完了していない場合があります。準備完了チェックを実装してください:
async function waitForCdpReady(host, port, maxAttempts = 30) {
for (let i = 0; i < maxAttempts; i++) {
try {
const response = await fetch(`http://${host}:${port}/json/version`);
if (response.ok) return true;
} catch (e) {
await new Promise(r => setTimeout(r, 1000));
}
}
throw new Error(`CDP not ready after ${maxAttempts} attempts`);
}
3. 動的なコンテナ名
ブラウザコンテナ名はランダムなサフィックスを付けて生成されます。DNS解決には一貫した命名プレフィックスを使用してください:
// In browser.ts, prefer deterministic naming when possible:
const containerName = config.customName || `openclaw-sandbox-browser`;
または、Dockerの --network-alias を使用して安定したエイリアスを作成します:
HostConfig: {
NetworkMode: 'openclaw-sandbox-browser',
DNS: ['openclaw-sandbox-browser-alias'] // Use this as CDP host
}
4. macOS Docker Desktopの特異性
WSL2搭載のDocker Desktopでは、host.docker.internal エクストラホストは実際のホストではなくWSL2 VMにマッピングされます。内部DNSアプローチはDockerのコンテナDNSを使用するため,依然として正しく機能します。
5. IPv6ループバック
一部のシステムでは ::1(IPv6ループバック)が 127.0.0.1(IPv4)よりも優先されています。CDP URLが明示的にIPv4を使用することを確認してください:
cdpUrl = "ws://127.0.0.1:9222"; // Explicit IPv4
// NOT: "ws://localhost:9222"; // May resolve to ::1
6. 共有環境でのポート衝突
同じホスト上に複数のブラウザコンテナが存在する場合は、異なるCDPポートを確保してください:
{
"browser": {
"profiles": {
"openclaw": {
"cdpPort": 29222 // Non-standard port to avoid conflicts
}
}
}
}
🔗 関連するエラー
関連する問題
- #52662 — External CDP endpoint configuration: 外部で管理されるブラウザのアタッチを可能にする `externalCdpEndpoint` 設定オプションの公開を提案しています。
attachOnlyモードは readiness probe がホストPIDベースであり、コンテナ起動のChromeを認識しないため失敗します。 - #58606 — Browser container starts but CDP port unreachable: この問題と同じ根本原因ですが、沙盒ネットワーキングの観点ではなくポート公開の観点から記述されています。
- #64383 — Remove socat CDP intermediate layer: CDP Bridging Proxyレイヤーの排除についての議論です。解決されればネットワーキングモデルが簡素化されますが、127.0.0.1のハードコーディングには直接対応しません。
関連するエラーメッセージ
| エラーコード/メッセージ | 説明 |
|---|---|
Chrome CDP websocket for profile “X” is not reachable after start | 主な症状;CDP接続のタイムアウトまたは拒否を示します |
PortInUseError: Port 9222 is already in use | attachOnly: true が設定されていないがブラウザがすでに実行されている場合に発生 |
Browser attachOnly is enabled and profile ‘X’ is not running | Readiness probeがコンテナ起動のChromeを検出できない |
Connection refused | CDPホストに到達できない場合のネットワークレベルでの失敗 |
ETIMEDOUT | エージェントコンテナがブラウザへのネットワークアクセスを持たない場合に発生 |
影響を受けるバージョン
- OpenClaw 2026.4.x: 影響が確認済み;
ensureSandboxBrowserが127.0.0.1をハードコーディング - OpenClaw 2026.3.x: 可能性がある;同じコードパス
- OpenClaw 2026.5.x: 修正保留中;次のマイナーリリースでパッチが予定されています
回避策の状況
修正がマージされるまでは、唯一完全に機能する回避策は次のとおりです:
{
"browser": {
"enabled": false
}
}
これによりブラウザツールが無効になり、すべてのHTTPベースのWebアクセスが web_fetch にフォールバックします。JavaScript実行やインタラクティブなクリックフローを必要とするサイトには適していません。