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: unavailableTechnisches Verhalten
- Der Befehl
memory statusmeldetEmbeddings: unavailableanstatt den konfigurierten Remote-Embedding-Anbieter anzuzeigen. - Der Befehl
memory searchschlä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_PROXYoderALL_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 environmentsWarum dies in Proxy-Umgebungen fehlschlägt
In Proxy-Konfigurationen wie Clash TUN mit fake-IP-Modus:
- Der DNS-Resolver der lokalen Maschine kann
api.ohmygpt.comnicht erreichen - Die DNS-Auflösung muss durch den Proxy-Tunnel erfolgen (z.B. via
clashDNSoder Proxy-seitige Auflösung) - Der
dns.lookup()-Aufruf verwendet den System-Resolver und umgeht den Proxy vollständig - 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 mainOption 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 -20Schritt 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 enthaltenSchritt 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 seinSchritt 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ückSchritt 7: Exit-Codes verifizieren:
$ openclaw memory search --agent main --query "test"
$ echo $?
0
# Erwartet: Exit-Code 0 bei ErfolgErwartete 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
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-Pfadauth-profiles-DDVivXkv.js— Alternatives Auth-Bundlediscord-CcCLMjHw.js— Discord-Kanalpfad
Betroffene Bundles, die möglicherweise bereits die Korrektur haben:
auth-profiles-DRjqKE3G.js— CLI-Pfadmodel-selection-*.js— Model-Selektions-Bundlesplugin-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
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
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:10804. Bundle-Cache nach Quellcode-Korrektur
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
Proxy in WSL explizit setzen:
# In WSL, Windows-Host-IP abrufen
export HTTPS_PROXY=http://$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):78906. localhost vs 127.0.0.1 in verschiedenen Kontexten
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, wenndns.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 Formathttp://user:password@host:portin 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ältfetchWithSsrFGuard(), die Kern-Fetch-Funktion mit Modus-Auflösungsrc/ssrf/fetch-mode.ts— EnthälthasProxyEnvConfigured()undwithTrustedEnvProxyGuardedFetchMode()src/ssrf/dns-resolve.ts— EnthältresolvePinnedHostnameWithPolicy(), das den blockierendendns.lookup()durchführtsrc/memory/post-json.ts— EnthältwithRemoteHttpResponse(), das die Korrektur benötigt