Sandboxed Agent kann Browser CDP nicht erreichen — 127.0.0.1 in ensureSandboxBrowser fest codiert
Wenn der Sandbox-Modus auf 'all' gesetzt ist, schlägt die CDP-Websocket-Verbindung fehl, weil ensureSandboxBrowser den CDP-Port auf den Host 127.0.0.1 abbildet, der vom Docker-Sandbox-Agent-Container aus nicht erreichbar ist.
🔍 Symptome
Primäre Fehlermanifestation
Wenn ein Agent in einem Docker-Container mit agents.defaults.sandbox.mode: "all" ausgeführt wird, führt jede Verwendung des browser-Tools zu einem Verbindungsfehler:
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 ...
Netzwerkdiagnose-Beweise
Von innerhalb des Sandbox-Agent-Containers aus zeigen Konnektivitätstests das grundlegende Problem:
# 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
Konfiguration, die das Problem auslöst
Die folgende minimale Konfiguration reproduziert den Fehler:
{
"agents": {
"defaults": {
"sandbox": {
"mode": "all",
"scope": "agent",
"workspaceAccess": "rw"
}
}
},
"tools": {
"sandbox": {
"tools": {
"alsoAllow": ["browser"]
}
}
}
}
Dokumentierte Workaround-Fehler
| Konfigurationsversuch | Beobachtetes Ergebnis |
|---|---|
Standard (kein docker.network, network: none) | Agent hat kein Netzwerk; CDP nicht erreichbar |
sandbox.docker.network: "openclaw-sandbox-browser" | Funktioniert immer noch nicht – Code verwendet 127.0.0.1 statt Container-DNS |
sandbox.docker.extraHosts: ["host.docker.internal:host-gateway"] | 127.0.0.1 zeigt immer noch auf Container-Loopback |
browser.profiles.remote-chrome.cdpUrl = "ws://127.0.0.1:9222" mit attachOnly: true | "Browser attachOnly is enabled and profile 'remote-chrome' is not running." |
Gleiches ohne attachOnly | "PortInUseError: Port 9222 is already in use." |
🧠 Ursache
Architektonische Übersicht
Die OpenClaw-Browser-Automatisierungsarchitektur umfasst zwei unterschiedliche Laufzeitkontexte:
- Browser Sidecar Container: Ein Docker-Container, der Chromium mit aktiviertem CDP ausführt und mit dem Docker-Netzwerk `openclaw-sandbox-browser` verbunden ist.
- Agent Sandbox Container: Ein optionaler Docker-Container, der den Agent-Code ausführt und möglicherweise ebenfalls mit dem `openclaw-sandbox-browser`-Netzwerk verbunden ist.
Die fehlerhafte Port-Mapping-Strategie
Die Funktion ensureSandboxBrowser in src/agents/sandbox/browser.ts erstellt den Browser-Container und veröffentlicht den CDP-Port mit diesem Muster:
// 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 };
}
Warum 127.0.0.1 innerhalb eines Containers fehlschlägt
Die Netzwerktopologie unterscheidet sich je nach Client-Standort:
| Client-Standort | 127.0.0.1 aufgelöst zu | Browser-Container erreichbar? |
|---|---|---|
| Host-Maschine | Host-Loopback-Interface | Ja (via Port-Binding) |
| Agent-Container | Loopback des Agent-Containers | Nein (separater Netzwerk-Namespace) |
Der Port des Browser-Containers ist an 127.0.0.1 im Host-Netzwerk-Namespace gebunden. Der Agent-Container hat sein eigenes isoliertes Loopback-Interface – Datenverkehr zu 127.0.0.1 von innerhalb des Agent-Containers verlässt diesen Namespace niemals.
Die fehlende Logik
Dem Code fehlt eine bedingte Logik, um zu erkennen, wann:
- Der Agent innerhalb eines Docker-Containers läuft
- Der Agent-Container dasselbe Docker-Netzwerk wie der Browser-Container teilt
- Die CDP-URL daher die interne DNS-Auflösung von Docker verwenden sollte
Die benutzerdefinierten Bridge-Netzwerke von Docker bieten automatische DNS-Auflösung zwischen Containern anhand ihrer Containernamen. Wenn der Agent-Container sich auf openclaw-sandbox-browser befindet und der Browser-Containername openclaw-sandbox-browser-abc123 lautet, kann der Agent den Browser über diesen DNS-Namen auf jedem Port erreichen.
Code-Pfad-Analyse
agent.sandbox.run() └── ensureSandboxBrowser() ├── Erstellt Browser-Container im openclaw-sandbox-browser-Netzwerk ├── Bindet Port an 127.0.0.1 (nur Host) └── Gibt cdpUrl = “ws://127.0.0.1:9222” zurück
browser-tool.client.connect() └── Versucht Verbindung zur cdpUrl herzustellen └── Falls Agent containerisiert ist: Verbindung abgelehnt (Container-Loopback)
🛠️ Schritt-für-Schritt-Lösung
Voraussetzungen der Konfiguration
Bevor Sie die Lösung anwenden, stellen Sie sicher, dass die Agent-Sandbox konfiguriert ist, um dem Browser-Container-Netzwerk beizutreten. Fügen Sie Folgendes zu Ihrer OpenClaw-Konfiguration hinzu:
{
"agents": {
"defaults": {
"sandbox": {
"mode": "all",
"scope": "agent",
"workspaceAccess": "rw",
"docker": {
"network": "openclaw-sandbox-browser"
}
}
}
}
}
Die Lösung: Ändern von ensureSandboxBrowser zur Verwendung von Container-DNS
Finden Sie src/agents/sandbox/browser.ts und wenden Sie den folgenden Patch an:
Vorher (problematisch):
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 };
}
Nachher (korrigiert):
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 };
}
Alternative: Umgebungsbasierte Konfiguration (Non-Code-Lösung)
Wenn das Ändern des Quellcodes nicht möglich ist, konfigurieren Sie das Browser-Tool so, dass der interne Endpunkt des Containers verwendet wird, indem Sie die CDP-URL direkt festlegen:
{
"browser": {
"profiles": {
"openclaw": {
"cdpUrl": "ws://openclaw-sandbox-browser-{{CONTAINER_ID}}:9222",
"launchOptions": {
"args": ["--disable-web-security"]
}
}
}
}
}
Hinweis: Dies erfordert die Kenntnis des Browser-Containernamens zum Zeitpunkt der Konfiguration. Der Containername wird dynamisch generiert; verwenden Sie ein konsistentes Namenspräfix oder inspizieren Sie mit docker ps --filter name=openclaw-sandbox-browser.
Docker-Netzwerk-Verifizierung
Stellen Sie sicher, dass das Netzwerk existiert, bevor Sie OpenClaw starten:
# 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
🧪 Verifizierung
Schritt 1: Überprüfen, ob der Browser-Container erfolgreich startet
$ 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
Schritt 2: Bestätigen, dass der Agent-Container sich im selben Netzwerk befindet
$ docker inspect openclaw-agent-xyz789 --format '{{range $k, $v := .NetworkSettings.Networks}}{{$k}}{{end}}'
openclaw-sandbox-browser
Schritt 3: CDP-Konnektivität vom Agent-Container aus testen
$ 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 ..."
}
Schritt 4: WebSocket-Upgrade verifizieren
$ 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
Schritt 5: Funktionstest mit OpenClaw
Führen Sie eine browser-basierte Aufgabe über die CLI oder API aus:
$ 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
Schritt 6: Bestätigen, dass der Host-Zugriff weiterhin funktioniert
Von der Host-Maschine aus (nicht innerhalb eines Containers):
$ curl -s http://127.0.0.1:9222/json/version | head -1
{
"Browser": "Chromium/120.0.6099.109",
Erwartet: Sowohl der Host (127.0.0.1) als auch der interne Container-DNS (openclaw-sandbox-browser-*) sind erreichbar.
⚠️ Häufige Fehler
1. Netzwerkmodus-Mismatch
Die Agent-Sandbox muss ausdrücklich dem openclaw-sandbox-browser-Netzwerk beitreten. Wenn dies weggelassen wird, verwendet der Agent standardmäßig network: none (kein Netzwerk) oder das Standard-Bridge-Netzwerk.
// FALSCH: Fehlende Netzwerkkonfiguration
"sandbox": {
"mode": "all"
// Agent hat kein Netzwerk oder falsches Netzwerk
}
// RICHTIG: Explizite Netzwerk-Anbindung
"sandbox": {
"mode": "all",
"docker": {
"network": "openclaw-sandbox-browser"
}
}
2. Race Condition beim Container-Start
Der Browser-Container ist möglicherweise noch nicht vollständig bereit, wenn der Agent versucht, eine CDP-Verbindung herzustellen. Implementieren Sie einen Readiness-Check:
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. Dynamische Containernamen
Browser-Containernamen werden mit zufälligen Suffixen generiert. Verwenden Sie ein konsistentes Namenspräfix für die DNS-Auflösung:
// In browser.ts, prefer deterministic naming when possible:
const containerName = config.customName || `openclaw-sandbox-browser`;
Alternativ können Sie Dockss --network-alias verwenden, um einen stabilen Alias zu erstellen:
HostConfig: {
NetworkMode: 'openclaw-sandbox-browser',
DNS: ['openclaw-sandbox-browser-alias'] // Use this as CDP host
}
4. Docker Desktop für macOS Eigenheiten
Bei Docker Desktop mit WSL2 wird der Extra-Host host.docker.internal zur WSL2-VM zugeordnet, nicht zum eigentlichen Host. Der interne DNS-Ansatz funktioniert weiterhin korrekt, da er Dockss Container-DNS verwendet.
5. IPv6 Loopback
Einige Systeme haben ::1 (IPv6 Loopback) über 127.0.0.1 (IPv4) priorisiert. Stellen Sie sicher, dass die CDP-URL explizit IPv4 verwendet:
cdpUrl = "ws://127.0.0.1:9222"; // Explizites IPv4
// NICHT: "ws://localhost:9222"; // Könnte zu ::1 aufgelöst werden
6. Port-Konflikte in gemeinsamen Umgebungen
Wenn mehrere Browser-Container auf demselben Host existieren, stellen Sie sicher, dass unterschiedliche CDP-Ports verwendet werden:
{
"browser": {
"profiles": {
"openclaw": {
"cdpPort": 29222 // Non-standard port to avoid conflicts
}
}
}
}
🔗 Zugehörige Fehler
Verbundene Probleme
- #52662 — External CDP endpoint configuration: Schlägt vor, eine `externalCdpEndpoint`-Konfigurationsoption bereitzustellen, um das Anhängen eines extern verwalteten Browsers zu ermöglichen. Der Modus `attachOnly` schlägt fehl, weil der Readiness-Probe host-PID-basiert ist und den von einem Container gestarteten Chrome nicht erkennt.
- #58606 — Browser container starts but CDP port unreachable: Gleiche Ursache wie dieses Problem, jedoch aus der Perspektive der Port-Exposition statt der Sandbox-Netzwerkperspektive betrachtet.
- #64383 — Remove socat CDP intermediate layer: Diskussion über die Eliminierung der CDP-Bridging-Proxy-Schicht. Wenn gelöst, würde dies das Netzwerkmodell vereinfachen, adressiert aber nicht direkt die Hartcodierung von 127.0.0.1.
Zugehörige Fehlermeldungen
| Fehlercode/Nachricht | Beschreibung |
|---|---|
Chrome CDP websocket for profile “X” is not reachable after start | Primäres Symptom; zeigt CDP-Verbindungs-Timeout oder -Ablehnung an |
PortInUseError: Port 9222 is already in use | Tritt auf, wenn attachOnly: true nicht gesetzt ist, aber bereits ein Browser läuft |
Browser attachOnly is enabled and profile ‘X’ is not running | Readiness-Probe erkennt den container-gestarteten Chrome nicht |
Connection refused | Netzwerkfehler auf Verbindungsebene, wenn CDP-Host nicht erreichbar ist |
ETIMEDOUT | Tritt auf, wenn Agent-Container keinen Netzwerkzugang zum Browser hat |
Betroffene Versionen
- OpenClaw 2026.4.x: Bestätigt betroffen;
ensureSandboxBrowsercodiert127.0.0.1fest - OpenClaw 2026.3.x: Wahrscheinlich betroffen; gleicher Code-Pfad
- OpenClaw 2026.5.x: Lösung ausstehend; Patch in der nächsten Minor-Version erwartet
Workaround-Status
Bis die Lösung zusammengeführt wird, ist der einzige vollständig funktionierende Workaround:
{
"browser": {
"enabled": false
}
}
Dies deaktiviert das Browser-Tool vollständig und verwendet web_fetch für den gesamten HTTP-basierten Webzugriff. Dies ist ungeeignet für Sites, die JavaScript-Ausführung oder interaktive Click-Through-Flows erfordern.