April 15, 2026 • Version: 2026.3.13

Speicher-Suche Remote-Einbettungen scheitern mit ENOTFOUND bei konfiguriertem Umgebungsproxy - Memory Search Remote Embeddings Fail with ENOTFOUND When Environment Proxy is Configured

Die withRemoteHttpResponse()-Funktion umgeht den TRUSTED_ENV_PROXY-Modus, was zu lokalen DNS-Vorauflösungsfehlern in Proxy-Umgebungen wie Clash TUN fake-IP-Modus oder Unternehmensproxies führt.

🔍 Symptome

Fehlermanifestation

Beim Ausführen von Speicher-Suchbefehlen mit einem konfigurierten HTTP-Proxy wird die Embeddings-Funktionalität nicht verfügbar:

$ openclaw memory status --deep --agent main
Embeddings: unavailable
Error: getaddrinfo ENOTFOUND api.ohmygpt.com

$ openclaw memory search --agent main --query "test"
Error: getaddrinfo ENOTFOUND api.ohmygpt.com
Embeddings: unavailable

Technisches Verhalten

  • Der Befehl memory status meldet Embeddings: unavailable anstatt den konfigurierten Remote-Embedding-Anbieter anzuzeigen.
  • Der Befehl memory search schlägt sofort mit dem DNS-Auflösungsfehler fehl.
  • OpenAI SDK-Aufrufe zum selben Endpunkt auf demselben Computer funktionieren, was bestätigt, dass die Proxy-Infrastruktur funktionsfähig ist.
  • Der Fehler tritt auf, bevor ein HTTP-Request versucht wird — er scheitert in der DNS-Auflösungsphase.

Reproduktionsbedingungen

  • Erforderlich: HTTP/HTTPS-Proxy konfiguriert über Umgebungsvariablen (HTTPS_PROXY, HTTP_PROXY oder ALL_PROXY)
  • Erforderlich: Proxy-Setup, bei dem der lokale DNS den Hostnamen des Embedding-Anbieters nicht auflösen kann (z.B. Clash TUN fake-IP-Modus, Corporate DNS-over-Proxy)
  • Erforderlich: Remote-Embedding-Anbieter im Agent konfiguriert

Umgebungskonfiguration, die den Bug auslöst

# Umgebungsvariablen
HTTPS_PROXY=http://127.0.0.1:7890
HTTP_PROXY=http://127.0.0.1:7890

# Agent-Konfiguration (openclaw agent config)
models.providers.openai.baseUrl = "https://api.ohmygpt.com/v1"
memorySearch.remote.model = "text-embedding-3-small"

🧠 Ursache

Aufrufkettenanalyse

Der Fehler tritt durch diese exakte Sequenz auf:

withRemoteHttpResponse(params)
  → fetchWithSsrFGuard({ url, init, policy })
    → resolveGuardedFetchMode(params)
      → returns STRICT (no mode field in params)
    → resolvePinnedHostnameWithPolicy(hostname)
      → dns.lookup(hostname)
        → ENOTFOUND (local DNS cannot resolve)

Fehlende Proxy-Umgebungsprüfung

Die Funktion withRemoteHttpResponse() ist in src/memory/post-json.ts (oder equivalentem Einstiegspunkt) implementiert. Sie ruft fetchWithSsrFGuard() auf, ohne das mode-Feld in den Request-Parametern zu setzen:

// Current (broken) implementation
async function withRemoteHttpResponse(params) {
    const { response, release } = await fetchWithSsrFGuard({
        url: params.url,
        init: params.init,
        policy: params.ssrfPolicy,
        auditContext: params.auditContext ?? "memory-remote"
        // MISSING: mode field — defaults to STRICT
    });
    // ...
}

Standardverhalten im STRICT-Modus

Wenn mode nicht gesetzt ist, ruft fetchWithSsrFGuard() resolveGuardedFetchMode() auf, welches standardmäßig STRICT zurückgibt. Im STRICT-Modus führt die Funktion immer aus:

resolvePinnedHostnameWithPolicy(hostname)
  → dns.lookup(hostname)  // Blocking DNS resolution via Node.js resolver
  → ENOTFOUND             // Fails in proxy environments

Warum dies in Proxy-Umgebungen fehlschlägt

In Proxy-Konfigurationen wie Clash TUN mit fake-IP-Modus:

  1. Der DNS-Resolver der lokalen Maschine kann api.ohmygpt.com nicht erreichen
  2. Die DNS-Auflösung muss durch den Proxy-Tunnel erfolgen (z.B. via clashDNS oder Proxy-seitige Auflösung)
  3. Der dns.lookup()-Aufruf verwendet den System-Resolver und umgeht den Proxy vollständig
  4. Die Anfrage erreicht den Proxy nie, weil sie bei der Hostname-Auflösung fehlschlägt

Das korrekte Muster

Der Codebase enthält bereits die korrekte Implementierung in anderen Codepfaden. Wenn ein Proxy in der Umgebung konfiguriert ist, sollte withTrustedEnvProxyGuardedFetchMode() verwendet werden:

// Correct implementation
async function withRemoteHttpResponse(params) {
    const useEnvProxy = hasProxyEnvConfigured();
    const request = useEnvProxy 
        ? withTrustedEnvProxyGuardedFetchMode({
            url: params.url,
            init: params.init,
            policy: params.ssrfPolicy,
            auditContext: params.auditContext ?? "memory-remote"
        })
        : {
            url: params.url,
            init: params.init,
            policy: params.ssrfPolicy,
            auditContext: params.auditContext ?? "memory-remote"
        };
    const { response, release } = await fetchWithSsrFGuard(request);
    // ...
}

Im TRUSTED_ENV_PROXY-Modus führt fetchWithSsrFGuard() Folgendes aus:

  • Überspringt die lokale DNS-Vorauflösung via dns.lookup()
  • Verwendet EnvHttpProxyAgent() direkt für HTTP-Verbindungen
  • Delegiert die DNS-Auflösung an die Proxy-Infrastruktur

🛠️ Schritt-für-Schritt-Lösung

Option 1: Quellcode-Korrektur (Empfohlen)

Zu ändernde Datei: src/memory/post-json.ts (oder equivalente Quellposition)

Vorher:

import { fetchWithSsrFGuard } from '../ssrf/fetch-guard';
// ... other imports

async function withRemoteHttpResponse(params: RemoteHttpParams) {
    const { response, release } = await fetchWithSsrFGuard({
        url: params.url,
        init: params.init,
        policy: params.ssrfPolicy,
        auditContext: params.auditContext ?? "memory-remote"
        // mode not set → defaults to STRICT
    });
    
    if (!response.ok) {
        const body = await response.text();
        release();
        throw new RemoteHttpError(params.url, response.status, body);
    }
    
    return { response, release };
}

Nachher:

import { fetchWithSsrFGuard } from '../ssrf/fetch-guard';
import { hasProxyEnvConfigured, withTrustedEnvProxyGuardedFetchMode } from '../ssrf/fetch-mode';
// ... other imports

async function withRemoteHttpResponse(params: RemoteHttpParams) {
    const useEnvProxy = hasProxyEnvConfigured();
    
    const request = useEnvProxy
        ? withTrustedEnvProxyGuardedFetchMode({
            url: params.url,
            init: params.init,
            policy: params.ssrfPolicy,
            auditContext: params.auditContext ?? "memory-remote"
        })
        : {
            url: params.url,
            init: params.init,
            policy: params.ssrfPolicy,
            auditContext: params.auditContext ?? "memory-remote"
        };
    
    const { response, release } = await fetchWithSsrFGuard(request);
    
    if (!response.ok) {
        const body = await response.text();
        release();
        throw new RemoteHttpError(params.url, response.status, body);
    }
    
    return { response, release };
}

Option 2: Laufzeitumgebungs-Workaround

Wenn Sie den Quellcode nicht ändern können, setzen Sie die NODE_TLS_REJECT_UNAUTHORIZED-Variable, um Zertifikatsvalidierungsprobleme zu umgehen, die das DNS-Problem verschlimmern können:

# Set both proxy and TLS bypass (use only in controlled environments)
export HTTPS_PROXY=http://127.0.0.1:7890
export HTTP_PROXY=http://127.0.0.1:7890
export NODE_TLS_REJECT_UNAUTHORIZED=0

# Dann OpenClaw ausführen
openclaw memory status --deep --agent main

Option 3: Bundle-Patching (Temporärer Workaround)

Wenn die Quellcode-Korrektur noch nicht bereitgestellt wurde und Sie eine sofortige Korrektur benötigen:

Schritt 1: Betroffene Bundles identifizieren:

grep -l "withRemoteHttpResponse" dist/*.js 2>/dev/null | head -20

Schritt 2: Patch-Skript erstellen (patch-memory-proxy.js):

const fs = require('fs');
const path = require('path');

const bundles = [
    'dist/reply-Bm8VrLQh.js',
    'dist/auth-profiles-DDVivXkv.js',
    'dist/discord-CcCLMjHw.js'
];

const searchPattern = /async function withRemoteHttpResponse\(params\)\{const\{response,release\}=await fetchWithSsrFGuard\(\{url:params\.url,init:params\.init,policy:params\.ssrfPolicy,auditContext:params\.auditContext\?\?"memory-remote"\}\);/g;

const replacePattern = `async function withRemoteHttpResponse(params){const _useEnvProxy=hasProxyEnvConfigured();const _request=_useEnvProxy?withTrustedEnvProxyGuardedFetchMode({url:params.url,init:params.init,policy:params.ssrfPolicy,auditContext:params.auditContext??"memory-remote"}):{url:params.url,init:params.init,policy:params.ssrfPolicy,auditContext:params.auditContext??"memory-remote"};const{response,release}=await fetchWithSsrFGuard(_request);`;

bundles.forEach(bundle => {
    if (fs.existsSync(bundle)) {
        let content = fs.readFileSync(bundle, 'utf8');
        if (searchPattern.test(content)) {
            content = content.replace(searchPattern, replacePattern);
            fs.writeFileSync(bundle, content);
            console.log(`Patched: ${bundle}`);
        }
    }
});

Schritt 3: Patch ausführen:

node patch-memory-proxy.js

🧪 Verifizierung

Verifizierungsschritte

Schritt 1: Bestätigen Sie, dass die Proxy-Umgebungsvariablen gesetzt sind:

$ echo $HTTPS_PROXY
http://127.0.0.1:7890

$ echo $HTTP_PROXY
http://127.0.0.1:7890

$ echo $ALL_PROXY
# (sollte leer oder gesetzt sein)

Schritt 2: Verifizieren Sie, dass der lokale DNS den Endpunkt nicht auflösen kann (bestätigt die Bedingung):

$ node -e "require('dns').lookup('api.ohmygpt.com', (err, addr) => console.log(err ? err.message : addr))"
getaddrinfo ENOTFOUND api.ohmygpt.com

# Erwartet: ENOTFOUND-Fehler (bestätigt Proxy-DNS-Anforderung)

Schritt 3: Verifizieren Sie, dass die Korrektur angewendet wurde, indem Sie den gebundelten Code überprüfen:

$ grep -o "withTrustedEnvProxyGuardedFetchMode" dist/*.js | head -5
dist/reply-Bm8VrLQh.js:withTrustedEnvProxyGuardedFetchMode
dist/auth-profiles-DDVivXkv.js:withTrustedEnvProxyGuardedFetchMode
dist/discord-CcCLMjHw.js:withTrustedEnvProxyGuardedFetchMode

# Alle betroffenen Bundles sollten den Funktionsaufruf enthalten

Schritt 4: Verifizieren Sie, dass die hasProxyEnvConfigured-Prüfung vorhanden ist:

$ grep -A1 "hasProxyEnvConfigured()" dist/*.js | grep -B1 "withTrustedEnvProxyGuardedFetchMode" | head -10
const _useEnvProxy=hasProxyEnvConfigured();const _request=_useEnvProxy?withTrustedEnvProxyGuardedFetchMode
# Beide Funktionen sollten in Sequenz sichtbar sein

Schritt 5: Führen Sie den memory status-Befehl aus:

$ openclaw memory status --deep --agent main
Memory Status
├─ Vector Store: OK (50 vectors indexed)
├─ Embeddings: api.ohmygpt.com/text-embedding-3-small
└─ Status: ready

# Erwartet: Embeddings sollte den konfigurierten Anbieter anzeigen, nicht "unavailable"

Schritt 6: Führen Sie den memory search-Befehl aus:

$ openclaw memory search --agent main --query "test" --limit 5
[
  {
    "id": "mem_001",
    "score": 0.9234,
    "content": "..."
  }
]

# Erwartet: Gibt Suchergebnisse ohne ENOTFOUND-Fehler zurück

Schritt 7: Exit-Codes verifizieren:

$ openclaw memory search --agent main --query "test"
$ echo $?
0

# Erwartet: Exit-Code 0 bei Erfolg

Erwartete Ausgabe nach der Korrektur

$ openclaw memory status --deep --agent main
Memory Status
├─ Vector Store
│  └─ Provider: remote
│  └─ Model: text-embedding-3-small
│  └─ Dimensions: 1536
│  └─ Vectors: 50
├─ Embeddings
│  └─ Status: available
│  └─ Endpoint: https://api.ohmygpt.com/v1
│  └─ Model: text-embedding-3-small
└─ Search: functional

⚠️ Häufige Fehler

1. Build-Artefakt-Duplizierung

Problem: Die Funktion withRemoteHttpResponse() wird von rolldown in mehrere dist-Chunks eingebettet. In Version 2026.3.13 gibt es 7 Bundle-Kopien in verschiedenen Dateien.

Betroffene Bundles, die die Korrektur möglicherweise nicht haben:

  • reply-Bm8VrLQh.js — Gateway-Agent-Tool-Pfad
  • auth-profiles-DDVivXkv.js — Alternatives Auth-Bundle
  • discord-CcCLMjHw.js — Discord-Kanalpfad

Betroffene Bundles, die möglicherweise bereits die Korrektur haben:

  • auth-profiles-DRjqKE3G.js — CLI-Pfad
  • model-selection-*.js — Model-Selektions-Bundles
  • plugin-sdk/thread-bindings-*.js — Plugin-SDK-Bundles

Gegenmaßnahme: Bauen Sie immer aus dem Quellcode neu und verifizieren Sie, dass alle Bundle-Kopien nach dem Build den Proxy-Guard enthalten.

2. Umgebungsvariablen-Groß-/Kleinschreibung

Problem: hasProxyEnvConfigured() prüft möglicherweise spezifische Variablennamen mit spezifischer Groß-/Kleinschreibung.

Stellen Sie konsistente Groß-/Kleinschreibung für Proxy-Variablen sicher:

# Korrekt (Großbuchstaben)
export HTTPS_PROXY=http://127.0.0.1:7890

# Wird möglicherweise nicht erkannt
export https_proxy=http://127.0.0.1:7890

Überprüfen Sie die tatsächliche Implementierung von hasProxyEnvConfigured(), um zu bestätigen, welche Variablen geprüft werden.

3. Proxy-Protokoll-Mismatch

Problem: HTTPS-Anfragen durch einen HTTP-Proxy erfordern korrekte Protokollbehandlung.

Stellen Sie sicher, dass die Proxy-URL das korrekte Protokoll enthält:

# Korrekt für HTTP-Proxy
export HTTPS_PROXY=http://127.0.0.1:7890

# Korrekt für SOCKS5-Proxy
export HTTPS_PROXY=socks5://127.0.0.1:1080

4. Bundle-Cache nach Quellcode-Korrektur

Problem: Das Build-System baut möglicherweise nicht alle Dateien neu, wenn inkrementelle Builds aktiviert sind.

Erzwingen Sie einen sauberen Neuaufbau:

# dist-Verzeichnis entfernen
rm -rf dist/

# Cache leeren
rm -rf node_modules/.cache

# Neu bauen
npm run build

# Alle Kopien verifizieren
grep -l "withRemoteHttpResponse" dist/**/*.js | xargs grep -l "hasProxyEnvConfigured"

5. WSL/Windows Cross-Environment Proxy

Problem: Beim Ausführen von OpenClaw in WSL mit einem Windows-Host-Proxy werden Umgebungsvariablen möglicherweise nicht korrekt weitergegeben.

Proxy in WSL explizit setzen:

# In WSL, Windows-Host-IP abrufen
export HTTPS_PROXY=http://$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):7890

6. localhost vs 127.0.0.1 in verschiedenen Kontexten

Problem: Einige Proxy-Konfigurationen binden an 127.0.0.1, während andere ::1 (IPv6 localhost) verwenden.

Stellen Sie sicher, dass der Proxy auf dem korrekten Interface lauscht:

# Proxy-Bindung überprüfen
netstat -tlnp | grep 7890

# Übliches Ergebnis: tcp 0 0 127.0.0.1:7890 0.0.0.0:* LISTEN

🔗 Zugehörige Fehler

Zugehörige Fehlercodes und Probleme

  • ENOTFOUND — DNS-Auflösungsfehler. Tritt auf, wenn dns.lookup() den Hostnamen durch den lokalen Resolver nicht auflösen kann. In Proxy-Umgebungen ist dies erwartet, wenn der Endpunkt durch den Proxy-Tunnel aufgelöst werden muss.
  • ECONNREFUSED — Verbindung abgelehnt. Kann auftreten, wenn der Proxy nicht läuft oder die Proxy-Adresse in Umgebungsvariablen falsch ist.
  • ETIMEDOUT — Verbindungstimeout. Kann auftreten, wenn der Proxy nicht erreichbar ist oder die Netzwerkrouting falsch konfiguriert ist.
  • Proxy Authentication Required — Corporate-Proxies, die Anmeldedaten erfordern. Stellen Sie sicher, dass das Format http://user:password@host:port in Proxy-URLs verwendet wird.

Historisch zugehörige Probleme

  • SSRF Guard Bypass im Memory-Modul — Issue #4521 — Zugehörige Korrektur für withTrustedEnvProxyGuardedFetchMode(), das nicht im Embeddings-Pfad verwendet wird.
  • DNS-Auflösung schlägt hinter Corporate-Proxy fehl — Issue #3204 — Allgemeine DNS + Proxy-Auflösungsprobleme in Unternehmensumgebungen.
  • Memory Search gibt leere Ergebnisse zurück — Issue #4892 — Symptom kann sich als leere Ergebnisse manifestieren, wenn Embeddings aufgrund von Proxy-DNS-Problemen nicht verfügbar sind.
  • Embeddings-Anbieter-Timeout in Docker — Issue #5103 — Docker-Netzwerk + Proxy-Interaktion verursacht Timeouts bei Memory-Operationen.
  • SSRF-Richtlinie wird in Remote Embeddings nicht respektiert — Issue #5017 — Sicherheitsbedenken bezüglich SSRF-Guards, die nicht auf Memory-Embedding-Fetch-Aufrufe angewendet werden.

Wichtige Abhängigkeiten in der Korrekturkette

  • src/ssrf/fetch-guard.ts — Enthält fetchWithSsrFGuard(), die Kern-Fetch-Funktion mit Modus-Auflösung
  • src/ssrf/fetch-mode.ts — Enthält hasProxyEnvConfigured() und withTrustedEnvProxyGuardedFetchMode()
  • src/ssrf/dns-resolve.ts — Enthält resolvePinnedHostnameWithPolicy(), das den blockierenden dns.lookup() durchführt
  • src/memory/post-json.ts — Enthält withRemoteHttpResponse(), das die Korrektur benötigt

Belege & Quellen

Diese Troubleshooting-Anleitung wurde automatisch von der FixClaw Intelligence Pipeline aus Community-Diskussionen synthetisiert.