Ihr Node.js-Skript ruft seit Wochen alle zehn Minuten dieselbe API auf. Sie verschieben es auf einen CI-Runner, oder ein Kollege startet es im Büronetz, und plötzlich endet jeder Aufruf mit einer einzigen Zeile: TypeError: fetch failed. Kein Hostname, kein Statuscode. Die URL öffnet sich im Browser, und curl erreicht sie vom selben Rechner aus, also scheint die Meldung nichts zu sagen.
Sie sagt durchaus etwas, nur nicht in der Meldung selbst. Dieser Leitfaden zeigt, wo Node.js den eigentlichen Grund ablegt, welche Ursachen wir unter Node.js 22, 24 und 26 nachgestellt haben, warum fetch Proxy-Variablen ignoriert, welche undici-Inkompatibilität unter Node.js 26 Proxy-Agents lahmlegt, und ein getestetes Skript, das die Ursache benennt.
Was bedeutet „TypeError: fetch failed“?
Das globale fetch() von Node.js, verfügbar seit Version 18, basiert auf undici, einem HTTP-Client, der in jeder Node.js-Version mitgeliefert wird. Der Fetch-Standard legt fest, dass eine Anfrage, die mit einem Netzwerkfehler endet, mit einem TypeError zurückgewiesen wird. Undici verwendet für alle diese Fehler dieselbe feste Meldung, fetch failed, und hängt den eigentlichen Fehler als cause an.
Ein Netzwerkfehler ist alles, was vor einer Antwort schiefgeht: die Namensauflösung, die TCP-Verbindung, der TLS-Handshake, der Proxy-Tunnel oder eine Verbindung, die sich schließt, bevor die Header ankommen. Ein 404 oder 500 gehört nicht dazu; fetch wird normal erfüllt, und res.ok ist false.
Fängt niemand den Fehler ab, gibt Node.js die Ursache selbst aus. Das ist die echte Ausgabe eines einzeiligen Skripts mit falsch geschriebenem Host unter Node.js 26.10.0:
[TypeError: fetch failed] {
[cause]: Error: getaddrinfo ENOTFOUND api.example-typo.invalid
at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:122:26) {
errno: -3008,
code: 'ENOTFOUND',
syscall: 'getaddrinfo',
hostname: 'api.example-typo.invalid'
}
}Schwierig wird es, wenn Code den Fehler abfängt und nur die Meldung protokolliert, wie es viele Frameworks und SDKs tun. Die Ursache auszugeben kostet eine Zeile mehr:
try {
await fetch("https://api.example-typo.invalid/v1/items");
} catch (err) {
console.log(err.message); // fetch failed
console.log(err.cause?.code, err.cause?.message); // ENOTFOUND getaddrinfo ENOTFOUND api.example-typo.invalid
}Wie finden Sie den echten Fehler hinter fetch failed?
- Protokollieren Sie
err.cause, nichterr.message. Verschluckt ein Framework die Ursache, umschließen Sie den Aufruf selbst. - Lesen Sie
cause.code. Codes, die mitEbeginnen (ENOTFOUND,ECONNRESET), stammen vom Betriebssystem, Codes mitUND_ERR_von undici und Codes wieUNABLE_TO_GET_ISSUER_CERT_LOCALLYvon der TLS-Schicht. - Achten Sie auf einen
AggregateError. Fürlocalhostprobiert Node.js mehrere Adressen. In unserem Test warcause.messageleer, undcause.errorsenthielt je einECONNREFUSEDfür::1und für127.0.0.1. - Gehen Sie bei Proxy-Fehlern eine Ebene tiefer. Eine abgelehnte Proxy-Anmeldung ergab
Request was cancelled.ohne Code; der Status stand inerr.cause.cause:Proxy response (407) !== 200 when HTTP Tunneling. - Achten Sie auf die Dauer. Millisekunden deuten auf eine Ablehnung, einen Reset oder eine DNS-Antwort hin; rund 10 Sekunden sind das Verbindungs-Timeout, fünf Minuten die Standardwartezeit auf die Header.
err.cause-Codes: was sie bedeuten und was zu beheben ist
Jede Zeile haben wir unter Windows 11 mit Node.js 24.21.0 und 26.10.0 erzeugt, mit einem lokalen Test-Proxy und lokalen Servern, die Verbindungen ablehnen, zurücksetzen, hängen lassen oder Testzertifikate vorlegen; beide Versionen lieferten dieselben Codes. Die Fehlerreferenz von undici dokumentiert die UND_ERR_-Codes und rät, auf error.code zu prüfen statt mit instanceof, da der Dispatcher aus einer anderen undici-Kopie stammen kann.
cause.code und Meldung | Was passiert ist | Zuerst prüfen |
|---|---|---|
ENOTFOUND getaddrinfo ENOTFOUND host | Der Hostname lässt sich nicht auflösen | Schreibweise, DNS, VPN |
ECONNREFUSED connect ECONNREFUSED 127.0.0.1:8999 | Auf diesem Port lauscht niemand | Dienst, Port, Proxy-Port |
ECONNREFUSED in einem AggregateError | Dasselbe, für jede Adresse von localhost | Server starten |
UND_ERR_CONNECT_TIMEOUT Connect Timeout Error | Keine TCP-Verbindung innerhalb von 10 s | Adresse, Firewall |
ECONNRESET read ECONNRESET | Per TCP-Reset abgebrochen | Proxy, Firewall; wiederholen |
UND_ERR_SOCKET other side closed | Vor jeder Antwort geschlossen | Server-Logs; wiederholen |
UND_ERR_HEADERS_TIMEOUT Headers Timeout Error | Verbunden, aber keine Header rechtzeitig | Ihr Zeitlimit |
UNABLE_TO_GET_ISSUER_CERT_LOCALLY, SELF_SIGNED_CERT_IN_CHAIN | Stammzertifikat nicht vertrauenswürdig | Stammzertifikat der Firma |
UNABLE_TO_VERIFY_LEAF_SIGNATURE | Server hat kein Zwischenzertifikat gesendet | Zertifikatskette des Servers |
ERR_SSL_WRONG_VERSION_NUMBER | TLS an einen reinen HTTP-Port gesendet | https:// in der Proxy-URL |
UND_ERR_INVALID_ARG invalid onError method | Agent aus einer anderen undici-Hauptversion | Das eigene fetch von undici |
| Kein Code: Request was cancelled. | Proxy hat den Tunnel abgelehnt | Proxy-Zugangsdaten |
Eine verwandte Meldung lautet nicht fetch failed: Kommen die Header an, aber der Body stockt, wirft res.text() oder res.json() den Fehler TypeError: terminated, in unserem Test mit UND_ERR_BODY_TIMEOUT als Ursache.
ENOTFOUND und ECONNREFUSED: Die Anfrage hat keinen Server erreicht
ENOTFOUND kommt aus der Namensauflösung: Der Resolver meldet, dass es den Namen nicht gibt. Suchen Sie nach einem Tippfehler, einem VPN mit eigenen DNS-Servern oder einem Container, der das interne DNS Ihres Unternehmens nicht erreicht. Hinter einem Proxy löst der Proxy den Namen auf, daher kommt ein falscher Host stattdessen als Statuscode des Proxys zurück: Unser Test-Proxy antwortete mit 502, gemeldet als Proxy response (502) !== 200 when HTTP Tunneling.
ECONNREFUSED bedeutet, dass der Rechner antwortet, auf diesem Port aber nichts Verbindungen annimmt. Steht in der Meldung die Adresse Ihres Proxys, ist die Proxy-Einstellung falsch, nicht das Ziel. Bei localhost probiert Node.js sowohl ::1 als auch 127.0.0.1; in unserem Test antwortete ein Server, der nur an 127.0.0.1 gebunden war, trotzdem auf http://localhost. Antwortet keine der beiden Adressen, ist der Entwicklungsserver aus oder nutzt einen anderen Port, oder Ihr Code läuft in Docker, wo localhost der Container selbst ist.
ECONNRESET, „other side closed“ und „socket hang up“
Alle drei bedeuten, dass eine Verbindung aufgebaut wurde und dann abbrach. Wir haben fetch mit dem http-Modul und Axios 1.20 verglichen:
- Ein Server, der mit einem TCP-Reset antwortete, erzeugte in allen dreien
read ECONNRESET. - Ein Server, der die Verbindung ohne Antwort schloss, erzeugte in fetch
UND_ERR_SOCKET(other side closed) und inhttpund Axiossocket hang upmit dem CodeECONNRESET.
Typische Gründe: Der Server ist mitten in der Anfrage abgestürzt, ein Load Balancer oder Proxy hat eine inaktive Keep-alive-Verbindung genau in dem Moment geschlossen, als Ihr Client sie wiederverwenden wollte, oder eine Firewall hat eine lange Verbindung gekappt. Manche Server und Bot-Filter schließen auch Verbindungen, die sie nicht bedienen wollen; dann heißt es langsamer werden oder um Zugang bitten, nicht hartnäckiger wiederholen.
Wiederholen Sie idempotente Anfragen (GET, HEAD) ein- oder zweimal mit wachsender Wartezeit. Wiederholen Sie nicht blind einen POST, der eine Bestellung anlegt: Der Server hat die Arbeit womöglich schon erledigt, bevor die Verbindung abbrach.
Timeouts: UND_ERR_CONNECT_TIMEOUT, UND_ERR_HEADERS_TIMEOUT und AbortSignal.timeout()
Fetch hat in Node.js kein Gesamtzeitlimit. Undici gibt einen TCP-Verbindungsaufbau nach 10 Sekunden auf, wartet dann bis zu 300 Sekunden auf die Header und bis zu 300 Sekunden zwischen zwei Teilen des Bodys. Ein Server, der die Verbindung annimmt und dann hängt, kann eine Anfrage fünf Minuten lang festhalten. Setzen Sie Ihr eigenes Limit:
try {
const res = await fetch("https://api.example.com/v1/items", { signal: AbortSignal.timeout(5_000) });
console.log(res.status);
} catch (err) {
if (err.name === "TimeoutError") console.log("gave up after 5 s");
else console.log(err.message, err.cause?.code);
}Gegen einen lokalen Server, der nie antwortet, gab der Code unter Node.js 24 und 26 gave up after 5 s aus. Wie MDN zu AbortSignal.timeout() erklärt, bricht das Signal mit einem TimeoutError ab, nicht mit fetch failed; prüfen Sie also err.name. Das Limit gilt auch für den Body: Ein hängender Body ließ res.text() denselben TimeoutError werfen. Um die eigenen Limits von undici zu ändern, brauchen Sie einen Agent aus dem npm-Paket, und damit stellt sich die Versionsfrage weiter unten.
Warum ignoriert fetch HTTP_PROXY und HTTPS_PROXY?
curl, pip und viele andere Tools lesen die Proxy-Umgebungsvariablen; das fetch von Node.js nicht. Mit gesetztem HTTPS_PROXY ging fetch unter Node.js 22.23.3, 24.21.0 und 26.10.0 direkt zum Ziel, und das Log unseres Proxys blieb leer, während curl den Proxy nutzte. Wo nur der Proxy ins Internet kommt, nennt die Ursache dann das Ziel (ENOTFOUND, UND_ERR_CONNECT_TIMEOUT), nie den übergangenen Proxy.
Die integrierte Proxy-Unterstützung von Node.js schaltet das ein: NODE_USE_ENV_PROXY=1 (ab Node.js 22.21.0 und 24.0.0) oder die Option --use-env-proxy (ab 22.21.0 und 24.5.0). Node.js liest dann beim Start HTTP_PROXY, HTTPS_PROXY und NO_PROXY, für fetch ebenso wie für die Module http und https. Die Dokumentation kennzeichnet die Funktion noch als in aktiver Entwicklung.
NODE_USE_ENV_PROXY=1 HTTPS_PROXY="http://user:pass@pr.proxynet.io:8000" NO_PROXY="localhost,127.0.0.1" node app.mjsIn PowerShell:
$env:NODE_USE_ENV_PROXY = "1"
$env:HTTPS_PROXY = "http://user:pass@pr.proxynet.io:8000"
node app.mjsUnser Test-Proxy protokollierte dann in allen drei Versionen CONNECT example.com:443; Node.js 22 warnte zusätzlich, dass EnvHttpProxyAgent experimentell ist. Drei Details sorgen oft für Verwirrung:
- Die Proxy-URL beginnt mit
http://, auch fürhttps://-Ziele. Die Tunnelanfrage ist reines HTTP, und TLS läuft darin;https://vor einem reinen HTTP-Proxy ergabERR_SSL_WRONG_VERSION_NUMBER. http.setGlobalProxyFromEnv()erledigt dasselbe aus dem Code heraus, ab Node.js 24.14.0 und 25.4.0; Node.js 22 fehlt die Funktion.- Ein falsches Passwort versteckt sich eine Ebene tiefer, in
err.cause.cause.
Die Gateway-Adresse eines Anbieters, bei unserem Residential-Proxy etwa http://user:pass@pr.proxynet.io:8000, kommt in dieselbe Variable; steht die IP Ihres Servers auf der IP-Whitelist, auch ohne Zugangsdaten.
Für Proxys pro Anfrage, Axios und SOCKS5 folgen Sie unserem Leitfaden Proxys in Node.js verwenden.
Node.js 26 und „invalid onError method“: die undici-Versionsinkompatibilität
Jede Node.js-Version bringt ihr eigenes undici mit, getrennt vom npm-Paket undici: Node.js 22.23.3 enthält undici 6.28.1, 24.21.0 enthält 7.29.1 und 26.10.0 enthält 8.10.2. Undici 8.0.0 hat die Wrapper entfernt, über die älterer Handler-Code mit neuerem Code sprechen konnte. Laut dem Release-Zeitplan von Node.js wird Node.js 26 am 28. Oktober 2026 zur Active-LTS-Linie, sodass viele Projekte beim Upgrade darauf stoßen werden.
Der Fehler tritt auf, wenn ein ProxyAgent oder Agent aus dem npm-Paket als dispatcher an das globale fetch übergeben wird. Wir haben vier npm-Hauptversionen unter drei Node.js-Versionen getestet:
| npm undici | Node.js 22.23.3 | Node.js 24.21.0 | Node.js 26.10.0 |
|---|---|---|---|
| 5.29.0 | funktioniert | funktioniert | invalid onError method |
| 6.29.0 | funktioniert | funktioniert | invalid onError method |
| 7.30.0 | funktioniert | funktioniert | funktioniert |
| 8.11.2 | invalid onRequestStart method | invalid onRequestStart method | funktioniert |
Jeder Fehlschlag war TypeError: fetch failed mit UND_ERR_INVALID_ARG als Ursache. Schlimmer ist eine stillere Variante: Unter Node.js 26 löste setGlobalDispatcher(new ProxyAgent(...)) aus undici 5 oder 6 keinen Fehler aus, fetch ignorierte den Dispatcher, und die Anfrage ging direkt hinaus, während unser Proxy nichts sah.
Die Lösung: Nehmen Sie fetch aus demselben Paket wie den Agent.
import { fetch, ProxyAgent } from "undici"; // fetch und der Agent aus demselben Paket
const proxy = new ProxyAgent("http://user:pass@pr.proxynet.io:8000");
const res = await fetch("https://example.com/", { dispatcher: proxy });
console.log(res.status); // 200 in unserem Test, über einen lokalen Test-ProxyDas funktionierte mit undici 8.11.2 unter Node.js 24 und 26. Steckt die Inkompatibilität in einem Tool, das Sie nicht selbst geschrieben haben, etwa einem CLI, das ein altes undici mitbringt und aus Ihren Proxy-Variablen einen Agent baut, aktualisieren Sie das Tool oder lassen Sie es auf Node.js 24, bis der Fehler behoben ist.
Zertifikatsfehler hinter fetch failed
Eine fehlgeschlagene TLS-Prüfung kommt ebenfalls als fetch failed an, mit dem Zertifikatscode in cause.code. Am Arbeitsplatz ist die übliche Quelle ein Firmen-Proxy oder Virenschutz, der HTTPS untersucht und den Datenverkehr mit einem eigenen Stammzertifikat neu signiert. Node.js prüft gegen seine eigene, mitgelieferte Liste von Stammzertifizierungsstellen, nicht gegen die des Betriebssystems, und lehnt dieses Stammzertifikat daher mit UNABLE_TO_GET_ISSUER_CERT_LOCALLY oder SELF_SIGNED_CERT_IN_CHAIN ab.
Lassen Sie NODE_EXTRA_CA_CERTS auf eine PEM-Datei mit dem Stammzertifikat der Firma zeigen, oder starten Sie Node.js mit --use-system-ca (ab 22.15.0 und 23.8.0), wenn das Stammzertifikat auf dem Rechner installiert ist. In unserem Test behob NODE_EXTRA_CA_CERTS den ersten Fall, aber nicht UNABLE_TO_VERIFY_LEAF_SIGNATURE: Dort lässt der Server sein Zwischenzertifikat weg, und nur sein Betreiber kann das beheben. Die Lösungen für npm, Git, Python und curl finden Sie in Unable to get local issuer certificate.
Ein fetch-Wrapper, der die Ursache nennt und wiederholt
Das Skript nutzt das globale fetch und braucht daher keine Pakete. Es durchläuft die ganze Ursachenkette, begrenzt jeden Versuch mit AbortSignal.timeout() und wiederholt nur Resets, geschlossene Sockets und Timeouts, mit exponentiellem Backoff plus Jitter. DNS-Fehler, abgelehnte Verbindungen, Zertifikatsfehler und eine abgelehnte Proxy-Anmeldung scheitern jedes Mal gleich, deshalb bricht es bei ihnen ab. Bei 429 und 503 wartet es so lange, wie Retry-After angibt.
Ein 429 ist die Bitte des Servers, langsamer zu werden; was der Header bedeutet und wie Sie das Tempo eines Jobs steuern, erklärt unser Artikel zu HTTP 429 Too Many Requests.
// fetch-check.mjs: nennt die echte Ursache hinter "TypeError: fetch failed" und wiederholt nur, was sich erholen kann.
// Getestet unter Node.js 22, 24 und 26. Optionaler Proxy: NODE_USE_ENV_PROXY=1 HTTPS_PROXY=http://user:pass@pr.proxynet.io:8000
const RETRY_CODES = new Set(["ECONNRESET", "UND_ERR_SOCKET", "UND_ERR_CONNECT_TIMEOUT"]);
const CERT_CODES = /CERT|SELF_SIGNED|UNABLE_TO_(GET|VERIFY)/;
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
/** Der Fehler und jeder darin verpackte Fehler, der äußerste zuerst. */
function causes(err) {
const chain = [];
for (let e = err; e && chain.length < 8; e = e.cause) {
chain.push(e);
if (e instanceof AggregateError) chain.push(...e.errors); // "localhost": ein Fehler pro Adresse
}
return chain;
}
/** Der erste Fehlercode als String in der Kette, etwa "ECONNRESET". */
const codeOf = (err) => causes(err).map((e) => e.code).find((c) => typeof c === "string") ?? "";
/** Eine Zeile: was fehlgeschlagen ist und was zuerst zu prüfen ist. */
function explain(err) {
if (err.name === "TimeoutError") return "no answer before our AbortSignal.timeout(): slow server or proxy";
const chain = causes(err);
const text = chain.map((e) => e.message).join(" | ");
const code = codeOf(err);
const tunnel = text.match(/Proxy response \((\d{3})\)/);
if (tunnel?.[1] === "407") return "proxy wants credentials (407): check user:pass or the IP whitelist";
if (tunnel) return `proxy refused the tunnel (${tunnel[1]}): the proxy could not or would not reach the target`;
if (code === "ENOTFOUND") return `name ${chain.find((e) => e.hostname)?.hostname} does not resolve: typo, DNS or VPN`;
if (code === "ECONNREFUSED") return "nothing listens on that address and port: wrong port, service down or wrong proxy port";
if (code === "ECONNRESET") return "the connection was cut (TCP reset): server, proxy or firewall dropped it";
if (code === "UND_ERR_SOCKET") return "the other side closed the connection before answering";
if (code === "UND_ERR_CONNECT_TIMEOUT") return "no TCP connection within 10 s: wrong IP, firewall or blocked outbound port";
if (code === "UND_ERR_HEADERS_TIMEOUT") return "connected, but no response headers in time";
if (code === "ERR_SSL_WRONG_VERSION_NUMBER") return "TLS spoken to a plain-HTTP port: the proxy URL should start with http://";
if (CERT_CODES.test(code)) return `certificate not trusted (${code}): add your CA with NODE_EXTRA_CA_CERTS`;
return `unrecognised, read the innermost error: ${chain.at(-1).message}`;
}
/** GET mit festem Zeitlimit, Backoff bei Netzwerkaussetzern und Retry-After bei 429/503. */
async function getWithRetry(url, { attempts = 3, timeoutMs = 15_000 } = {}) {
for (let attempt = 1; ; attempt++) {
const backoff = 500 * 2 ** (attempt - 1) + Math.random() * 250; // 0,5 s, 1 s, 2 s ... plus Jitter
let res;
try {
res = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });
} catch (err) {
const code = codeOf(err);
const retryable = RETRY_CODES.has(code) || err.name === "TimeoutError";
if (!retryable || attempt === attempts) throw err;
console.log(` attempt ${attempt}: ${code || err.name}, retrying in ${Math.round(backoff)} ms`);
await sleep(backoff);
continue;
}
if ((res.status === 429 || res.status === 503) && attempt < attempts) {
const wait = Number(res.headers.get("retry-after")) * 1000 || backoff; // nur die Angabe in Sekunden
await res.body?.cancel();
console.log(` attempt ${attempt}: HTTP ${res.status}, waiting ${Math.round(wait)} ms`);
await sleep(wait);
continue;
}
return res;
}
}
for (const url of process.argv.slice(2)) {
console.log(url);
try {
const res = await getWithRetry(url, { timeoutMs: 5_000 });
console.log(` OK: HTTP ${res.status}`);
} catch (err) {
console.log(` FAILED: ${err.name}: ${err.message} -> ${explain(err)}`);
}
}So sieht die Ausgabe aus
Wir haben das Skript unter Node.js 26.10.0 gegen einen falsch geschriebenen Host laufen lassen, gegen einen geschlossenen Port auf localhost und gegen lokale Server, die jede Verbindung zurücksetzen (Port 8902), nie antworten (8901), ein Zertifikat unserer eigenen Test-Zertifizierungsstelle nutzen (8906) und zweimal mit 429 antworten, bevor die Anfrage gelingt (8910):
https://api.example-typo.invalid/
FAILED: TypeError: fetch failed -> name api.example-typo.invalid does not resolve: typo, DNS or VPN
http://localhost:8999/
FAILED: TypeError: fetch failed -> nothing listens on that address and port: wrong port, service down or wrong proxy port
http://127.0.0.1:8902/
attempt 1: ECONNRESET, retrying in 652 ms
attempt 2: ECONNRESET, retrying in 1115 ms
FAILED: TypeError: fetch failed -> the connection was cut (TCP reset): server, proxy or firewall dropped it
http://127.0.0.1:8901/
attempt 1: TimeoutError, retrying in 658 ms
attempt 2: TimeoutError, retrying in 1024 ms
FAILED: TimeoutError: The operation was aborted due to timeout -> no answer before our AbortSignal.timeout(): slow server or proxy
https://127.0.0.1:8906/
FAILED: TypeError: fetch failed -> certificate not trusted (UNABLE_TO_GET_ISSUER_CERT_LOCALLY): add your CA with NODE_EXTRA_CA_CERTS
http://127.0.0.1:8910/items
attempt 1: HTTP 429, waiting 1000 ms
attempt 2: HTTP 429, waiting 1000 ms
OK: HTTP 200Dann über den lokalen Test-Proxy mit NODE_USE_ENV_PROXY=1: das richtige Passwort, ein falsches und https:// vor der Proxy-Adresse:
https://example.com/
OK: HTTP 200
https://example.com/
FAILED: TypeError: fetch failed -> proxy wants credentials (407): check user:pass or the IP whitelist
https://example.com/
FAILED: TypeError: fetch failed -> TLS spoken to a plain-HTTP port: the proxy URL should start with http://Node.js 22.23.3 und 24.21.0 gaben dieselben Zeilen aus, abgesehen von den zufälligen Backoff-Werten.
Wo Sie auf diesen Fehler stoßen
- Next.js und andere serverseitige Frameworks, deren Fehlerseiten oft nur die Meldung zeigen.
- MCP-Server und Tools für KI-Agenten, die oft hinter einem VPN oder Firmen-Proxy laufen.
- SDKs auf Basis von fetch, von denen einige in älteren Versionen die Ursache verworfen haben.
- CI-Runner und Docker-Builds, denen die Proxy-Variablen und das Stammzertifikat der Firma vom Host fehlen.
- Scraping- und Monitoring-Jobs, die bei langen Läufen den Großteil der Tabelle zu sehen bekommen.
Lange Erfassungsjobs profitieren am meisten von dem Wrapper. Arbeitet ein solcher Job mit wenigen bekannten Zielen und braucht Ausgangs-IPs, die über Wochen gleich bleiben, liefert Ihnen ein Datacenter-Proxy dedizierte IPv4-Adressen mit Traffic ohne Kontingent.
Häufige Fehler
err.messageprotokollieren. Dort steht immerfetch failed.- Erwarten, dass fetch
HTTPS_PROXYliest. Nicht ohneNODE_USE_ENV_PROXY=1oder--use-env-proxy. - Einen Agent aus npm-undici an das globale fetch übergeben. Importieren Sie
fetchaus demselben Paket. - Zertifikatsprüfungen abschalten.
NODE_TLS_REJECT_UNAUTHORIZED=0deaktiviert die Prüfung für den ganzen Prozess, sodass ein Angreifer auf dem Weg genauso aussieht wie Ihr Firmen-Proxy. - Alles wiederholen. Ein Tippfehler scheitert jedes Mal gleich; ein POST nach
ECONNRESETläuft womöglich zweimal. - Kein Zeitlimit. Ein hängender Server kann eine Anfrage fünf Minuten lang festhalten.
Entscheidungshilfe
Was Sie in err.cause sehen | Was zu tun ist |
|---|---|
ENOTFOUND | Hostnamen oder DNS korrigieren; nicht wiederholen |
ECONNREFUSED mit der Adresse Ihres Proxys | Proxy-Host und -Port neu kopieren |
ECONNREFUSED bei localhost | Server starten; Port und Docker prüfen |
ECONNRESET, UND_ERR_SOCKET | GET-Anfragen mit Backoff wiederholen |
UND_ERR_CONNECT_TIMEOUT | Firewall prüfen; hinter einem Firmen-Proxy NODE_USE_ENV_PROXY=1 setzen |
Lange Wartezeit, dann UND_ERR_HEADERS_TIMEOUT | AbortSignal.timeout() ergänzen |
| Ein Zertifikatscode | NODE_EXTRA_CA_CERTS oder --use-system-ca |
Proxy response (407) eine Ebene tiefer | user:pass oder die IP-Whitelist prüfen |
invalid onError method | fetch aus dem undici-Paket des Agents importieren |
Häufige Fragen
Warum wirft fetch bei einem 404 oder 500 keinen Fehler?
Weil der Server geantwortet hat. Fetch weist nur bei Netzwerkfehlern zurück; ein HTTP-Fehler wird mit res.ok gleich false erfüllt, prüfen Sie den Wert also, bevor Sie den Body lesen.
Ist „fetch failed“ dasselbe wie „Failed to fetch“ im Browser?
Verwandt, aber nicht dasselbe. Chrome weist mit TypeError: Failed to fetch zurück und sagt der Seite nicht, warum, sodass eine CORS-Sperre genauso aussieht; öffnen Sie in den DevTools den Tab Netzwerk. Node.js legt den Grund in cause ab.
Wie setzen Sie ein Timeout für fetch in Node.js?
Übergeben Sie signal: AbortSignal.timeout(ms). Wenn das Signal auslöst, weist fetch mit einem TimeoutError zurück, und das Limit stoppt auch einen hängenden Body.
Nutzt fetch in Node.js HTTP_PROXY und HTTPS_PROXY?
Nur mit NODE_USE_ENV_PROXY=1 (ab 22.21.0 und 24.0.0) oder --use-env-proxy (ab 22.21.0 und 24.5.0). Sonst werden die Variablen stillschweigend ignoriert.
Warum funktioniert curl, wenn fetch auf demselben Rechner scheitert?
Meist aus einem von zwei Gründen: curl liest die Proxy-Variablen und fetch nicht, und viele curl-Builds, auch der in Windows, nutzen den Zertifikatsspeicher des Systems, während Node.js seine eigene Liste verwendet, solange Sie nicht --use-system-ca übergeben.
Kann ein Proxy „TypeError: fetch failed“ beheben?
Nur wenn der Netzwerkweg die Ursache ist, etwa in einem Firmennetz, in dem nur der Proxy ins Internet kommt. Er behebt keinen Tippfehler, kein Zertifikatsproblem und keinen ausgefallenen Server, und er ist kein Weg, eine Website zu umgehen, die Ihre Verbindungen zurücksetzt oder per Rate Limit bremst: Werden Sie langsamer, nutzen Sie die offizielle API oder bitten Sie um Zugang.
Fazit
TypeError: fetch failed ist nur eine Hülle: Der Grund steht in err.cause, manchmal eine Ebene tiefer. Lesen Sie den Code, beheben Sie, was er nennt, geben Sie jedem Aufruf ein AbortSignal.timeout() und wiederholen Sie nur Resets und Timeouts. Setzen Sie hinter einem Proxy NODE_USE_ENV_PROXY=1, behalten Sie http:// in der Proxy-URL und nehmen Sie fetch und seinen Agent aus demselben undici-Paket, besonders unter Node.js 26. Wenn Ihr Code bereit für einen Proxy ist, vergleichen Sie die Optionen auf unserer Seite zu Proxy-Diensten.




