Node.js TypeError: fetch failed Hatası Nasıl Çözülür?

Yayın tarihi:

13 dk okuma

Acar Diveroli
Yazar: Acar Diveroli
Mavi imleçli await fetch(url) satırı; iz TLS'ten sonra kırmızı RESET işaretinde kopuyor, çıktıda cause: ECONNRESET yazıyor.

Node.js betiğiniz haftalardır on dakikada bir aynı API'yi çağırıyor. Betiği bir CI sunucusuna taşıyorsunuz ya da bir iş arkadaşınız ofis ağında çalıştırıyor; artık her çağrı tek bir satırla bitiyor: TypeError: fetch failed. Ne sunucu adı var ne durum kodu. Adres tarayıcıda açılıyor, aynı makineden curl da API'ye ulaşıyor; mesaj sanki hiçbir şey söylemiyor.

Aslında söylüyor, yalnız mesajın içinde değil. Bu yazıda Node.js'in asıl nedeni nerede tuttuğunu, Node.js 22, 24 ve 26'da yeniden ürettiğimiz nedenleri, fetch'in proxy değişkenlerini neden görmediğini, Node.js 26'da proxy agent'larını bozan undici uyuşmazlığını ve nedeni tek satırda söyleyen, test edilmiş bir betiği anlatıyoruz.

"TypeError: fetch failed" ne anlama gelir?

Node.js'te 18. sürümden beri bulunan global fetch(), her Node.js sürümünün içinde gelen undici adlı HTTP istemcisinin üzerine kuruludur. Fetch standardı, ağ hatasıyla biten bir isteğin TypeError ile reddedilmesini ister. Bu hataların hepsinde undici aynı mesajı, fetch failed metnini kullanır ve asıl hatayı cause özelliğine ekler.

Ağ hatası, yanıttan önce ters giden her şeyi kapsar: ad çözümleme, TCP bağlantısı, TLS el sıkışması, proxy tüneli ya da başlıklar gelmeden kapanan bir bağlantı. 404 ya da 500 ağ hatası sayılmaz; fetch normal biçimde sonuçlanır ve res.ok değeri false olur.

Hatayı yakalayan bir kod yoksa Node.js nedeni kendisi yazdırır. Aşağıda, Node.js 26.10.0'da adı yanlış yazılmış bir sunucuya istek atan tek satırlık bir betiğin gerçek çıktısını görüyorsunuz:

text
[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'
  }
}

Sorun, kod hatayı yakalayıp yalnız mesajı yazdırdığında başlar; pek çok çatı (framework) ve SDK da bunu yapar. Nedeni görmek için bir satır daha yeter:

js
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
}

fetch failed hatasının arkasındaki asıl hata nasıl bulunur?

  1. err.message değil, err.cause yazdırın. Kullandığınız çatı hatayı yutuyorsa çağrıyı kendiniz try/catch içine alın.
  2. cause.code değerini okuyun. E ile başlayan kodlar (ENOTFOUND, ECONNRESET) işletim sisteminden, UND_ERR_ ile başlayanlar undici'den, UNABLE_TO_GET_ISSUER_CERT_LOCALLY gibi kodlar TLS katmanından gelir.
  3. AggregateError olup olmadığına bakın. Node.js localhost için birden fazla adres dener. Testimizde cause.message boştu; cause.errors içinde biri ::1, biri 127.0.0.1 için iki ECONNREFUSED vardı.
  4. Proxy hatalarında bir kat daha inin. Proxy girişi reddedildiğinde cause alanında kodsuz bir Request was cancelled. vardı; durum kodu err.cause.cause içindeydi: Proxy response (407) !== 200 when HTTP Tunneling.
  5. Hatanın ne kadar sürede geldiğine bakın. Birkaç milisaniye ret, sıfırlama ya da DNS cevabı demektir; 10 saniye civarı bağlantı zaman aşımıdır; beş dakika, başlıklar için varsayılan bekleme süresidir.

err.cause kodları: hangisi ne anlama gelir, ne düzeltilir?

Tablodaki her satırı Windows 11'de Node.js 24.21.0 ve 26.10.0 ile, yerel bir test proxy'si ve bağlantıyı reddeden, sıfırlayan, bekleten ya da test sertifikası sunan yerel sunucularla ürettik; iki sürüm de aynı kodları verdi. undici'nin hata başvuru sayfası UND_ERR_ kodlarını açıklar ve kullanılan dispatcher başka bir undici kopyasından gelebileceği için instanceof yerine error.code ile eşleştirmeyi önerir.

cause.code ve mesajNe olduÖnce neye bakılır
ENOTFOUND getaddrinfo ENOTFOUND hostAlan adı çözülemiyorYazım, DNS, VPN
ECONNREFUSED connect ECONNREFUSED 127.0.0.1:8999O portta dinleyen yokServis, port, proxy portu
AggregateError içinde ECONNREFUSEDAynısı, localhost'un her adresi içinSunucuyu başlatın
UND_ERR_CONNECT_TIMEOUT Connect Timeout Error10 sn içinde TCP bağlantısı yokAdres, güvenlik duvarı
ECONNRESET read ECONNRESETTCP sıfırlamasıyla kesildiProxy, güvenlik duvarı; yeniden deneyin
UND_ERR_SOCKET other side closedYanıttan önce kapandıSunucu kayıtları; yeniden deneyin
UND_ERR_HEADERS_TIMEOUT Headers Timeout ErrorBağlandı, başlıklar zamanında gelmediKendi süre sınırınız
UNABLE_TO_GET_ISSUER_CERT_LOCALLY, SELF_SIGNED_CERT_IN_CHAINKök sertifikaya güvenilmiyorŞirketin kök sertifikası
UNABLE_TO_VERIFY_LEAF_SIGNATURESunucu ara sertifikayı göndermediSunucunun sertifika zinciri
ERR_SSL_WRONG_VERSION_NUMBERDüz HTTP portuna TLS gönderildiProxy URL'sindeki https://
UND_ERR_INVALID_ARG invalid onError methodBaşka bir undici sürümünün agent'ıundici'nin kendi fetch'i
Kod yok: Request was cancelled.Proxy tüneli reddettiProxy kimlik bilgileri

Benzer ama fetch failed olmayan bir mesaj daha var: başlıklar gelir de gövde takılırsa res.text() ya da res.json() TypeError: terminated fırlatır; testimizde nedeni UND_ERR_BODY_TIMEOUT idi.

ENOTFOUND ve ECONNREFUSED: istek hiçbir sunucuya ulaşmadı

ENOTFOUND ad çözümleme adımından gelir: DNS çözümleyici böyle bir ad olmadığını söylemiştir. Yazım hatasına, kendi DNS sunucularını kullanan bir VPN'e ya da şirketin iç DNS'ine ulaşamayan bir konteynere bakın. Proxy arkasında adı proxy çözer, bu yüzden hatalı bir alan adı proxy'nin durum kodu olarak geri döner: test proxy'miz 502 verdi, fetch de bunu Proxy response (502) !== 200 when HTTP Tunneling diye bildirdi.

ECONNREFUSED, makinenin cevap verdiğini ama o portta bağlantı kabul eden bir program olmadığını gösterir. Mesajdaki adres proxy'nizin adresiyse sorun hedefte değil, proxy ayarındadır. Node.js localhost için hem ::1 hem 127.0.0.1 adresini dener; testimizde yalnız 127.0.0.1'e bağlı bir sunucu http://localhost adresine yine cevap verdi. İkisi de cevap vermiyorsa geliştirme sunucusu kapalıdır, başka bir portu kullanıyordur ya da kodunuz Docker içinde çalışıyordur; orada localhost konteynerin kendisidir.

ECONNRESET, "other side closed" ve "socket hang up"

Üçü de açılan bir bağlantının sonradan koptuğunu anlatır. Testte fetch'i http modülü ve Axios 1.20 ile karşılaştırdık:

  • İsteğe TCP sıfırlamasıyla (RST) cevap veren bir sunucu, üç istemcide de read ECONNRESET üretti.
  • Bağlantıyı yanıt göndermeden kapatan bir sunucu fetch'te UND_ERR_SOCKET (other side closed), http ve Axios'ta ise ECONNRESET kodlu socket hang up üretti.

Sık görülen nedenler şunlardır: sunucunun istek sırasında çökmesi, bir yük dengeleyicinin ya da proxy'nin boştaki keep-alive bağlantısını tam istemciniz onu yeniden kullanırken kapatması ya da bir güvenlik duvarının uzun süren bağlantıyı düşürmesi. Bazı sunucular ve bot filtreleri de hizmet vermek istemedikleri bağlantıları kapatır; bu, daha sık denemenin değil, yavaşlamanın ya da erişim istemenin işaretidir.

Tekrarlandığında sonucu değiştirmeyen (idempotent) GET ve HEAD isteklerini artan aralıklarla bir iki kez yeniden deneyin. Sipariş oluşturan bir POST'u körü körüne tekrarlamayın: bağlantı kopmadan önce sunucu işi bitirmiş olabilir.

Zaman aşımları: UND_ERR_CONNECT_TIMEOUT, UND_ERR_HEADERS_TIMEOUT ve AbortSignal.timeout()

Node.js'te fetch'in toplam bir süre sınırı yoktur. TCP bağlantısı için undici 10 saniye bekler; bağlantı kurulduktan sonra başlıklar için 300 saniyeye, gövde parçaları arasında da 300 saniyeye kadar bekler. Bağlantıyı kabul edip takılan bir sunucu isteği beş dakika tutabilir. Kendi sınırınızı koyun:

js
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"); // 5 saniye doldu
  else console.log(err.message, err.cause?.code);
}

Hiç cevap vermeyen yerel bir sunucuya karşı bu kod Node.js 24 ve 26'da gave up after 5 s yazdırdı. MDN'nin AbortSignal.timeout() sayfasının belirttiği gibi sinyal TimeoutError ile iptal olur; hata fetch failed değildir, err.name ile kontrol edilir. Sınır gövdeyi de kapsar: takılan bir gövdede res.text() aynı TimeoutError hatasını verdi. Bu süreleri undici düzeyinde değiştirmek için npm paketinden bir Agent gerekir; bu da aşağıdaki sürüm sorusunu gündeme getirir.

fetch neden HTTP_PROXY ve HTTPS_PROXY değişkenlerini görmez?

curl, pip ve pek çok araç proxy ortam değişkenlerini kendiliğinden okur; Node.js'in fetch'i okumaz. HTTPS_PROXY tanımlıyken Node.js 22.23.3, 24.21.0 ve 26.10.0'da fetch doğrudan hedefe gitti ve proxy kaydımız boş kaldı; curl ise proxy'yi kullandı. İnternete yalnız proxy'nin çıkabildiği bir ağda cause hedefi gösterir (ENOTFOUND, UND_ERR_CONNECT_TIMEOUT), atlanan proxy'den hiç söz etmez.

Node.js'in yerleşik proxy desteği bunu açar: NODE_USE_ENV_PROXY=1 (Node.js 22.21.0 ve 24.0.0'dan itibaren) ya da --use-env-proxy seçeneği (22.21.0 ve 24.5.0'dan itibaren). Node.js bu durumda HTTP_PROXY, HTTPS_PROXY ve NO_PROXY değişkenlerini açılışta okur ve hem fetch'te hem http ile https modüllerinde kullanır. Belgeler özelliği hâlâ "aktif geliştirme" aşamasında gösteriyor.

bash
NODE_USE_ENV_PROXY=1 HTTPS_PROXY="http://user:pass@pr.proxynet.io:8000" NO_PROXY="localhost,127.0.0.1" node app.mjs

PowerShell'de:

powershell
$env:NODE_USE_ENV_PROXY = "1"
$env:HTTPS_PROXY = "http://user:pass@pr.proxynet.io:8000"
node app.mjs

Bundan sonra test proxy'miz üç sürümde de CONNECT example.com:443 kaydetti; Node.js 22 ayrıca EnvHttpProxyAgent özelliğinin deneysel olduğu uyarısını bastı. Üç ayrıntı sık atlanır:

  • Proxy URL'si https:// hedeflerde de http:// ile başlar. Tünel isteği düz HTTP'dir, TLS tünelin içinde çalışır; düz HTTP proxy'sinin önüne https:// yazınca ERR_SSL_WRONG_VERSION_NUMBER aldık.
  • Aynı işi koddan http.setGlobalProxyFromEnv() yapar. Node.js 24.14.0 ve 25.4.0'dan beri vardır; Node.js 22'de yoktur.
  • Yanlış şifre bir kat aşağıda saklanır, err.cause.cause içinde.

Residential Proxy hizmetimiz gibi bir sağlayıcının bağlantı adresi de aynı değişkene http://user:pass@pr.proxynet.io:8000 biçiminde yazılır; sunucunuzun IP'si IP whitelist'teyse kimlik bilgisine de gerek kalmaz.

İstek başına proxy, Axios ve SOCKS5 için Node.js'te proxy kullanımı rehberimizi izleyin.

Node.js 26 ve "invalid onError method": undici sürüm uyuşmazlığı

Her Node.js sürümü npm'deki undici paketinden ayrı, kendi undici kopyasını taşır: Node.js 22.23.3'te undici 6.28.1, 24.21.0'da 7.29.1, 26.10.0'da 8.10.2 var. 8.0.0 sürümünde undici, eski işleyici (handler) kodunun yeni kodla konuşmasını sağlayan sarmalayıcıları kaldırdı. Node.js sürüm takvimine göre Node.js 26, 28 Ekim 2026'da Active LTS hattı oluyor; pek çok proje bu hatayla yükseltme sırasında karşılaşacak.

Hata, npm paketinden oluşturulan bir ProxyAgent ya da Agent global fetch'e dispatcher olarak verildiğinde çıkar. Dört npm ana sürümünü üç Node.js sürümünde denedik:

npm undiciNode.js 22.23.3Node.js 24.21.0Node.js 26.10.0
5.29.0çalışıyorçalışıyorinvalid onError method
6.29.0çalışıyorçalışıyorinvalid onError method
7.30.0çalışıyorçalışıyorçalışıyor
8.11.2invalid onRequestStart methodinvalid onRequestStart methodçalışıyor

Her hata, nedeni UND_ERR_INVALID_ARG olan bir TypeError: fetch failed idi. Daha sessiz bir türü ise daha da kötü: Node.js 26'da undici 5 ya da 6'dan gelen setGlobalDispatcher(new ProxyAgent(...)) hiç hata vermedi, fetch onu yok saydı ve istek doğrudan çıktı; proxy'miz hiçbir şey görmedi.

Çözüm, fetch'i agent ile aynı paketten almaktır:

js
import { fetch, ProxyAgent } from "undici"; // fetch ve agent aynı paketten

const proxy = new ProxyAgent("http://user:pass@pr.proxynet.io:8000");
const res = await fetch("https://example.com/", { dispatcher: proxy });
console.log(res.status); // testimizde yerel bir test proxy'si üzerinden 200

Bu kod undici 8.11.2 ile Node.js 24 ve 26'da çalıştı. Uyuşmazlık sizin yazmadığınız bir araçtaysa, örneğin eski bir undici taşıyıp proxy değişkenlerinizden agent kuran bir CLI'daysa, aracı güncelleyin ya da düzeltme gelene kadar Node.js 24'te çalıştırın.

fetch failed hatasının arkasındaki sertifika hataları

Başarısız bir TLS doğrulaması da fetch failed olarak gelir; sertifika kodu cause.code içindedir. İş yerinde en sık kaynak, HTTPS trafiğini inceleyip kendi kök sertifikasıyla yeniden imzalayan bir şirket proxy'si ya da antivirüstür. Node.js sertifikaları işletim sisteminin listesine değil, kendi içinde gelen kök sertifika listesine göre doğrular; bu yüzden o kökü UNABLE_TO_GET_ISSUER_CERT_LOCALLY ya da SELF_SIGNED_CERT_IN_CHAIN ile reddeder.

NODE_EXTRA_CA_CERTS değişkenini şirketin kök sertifikasını içeren bir PEM dosyasına yönlendirin; kök makinede zaten kuruluysa Node.js'i --use-system-ca ile çalıştırın (22.15.0 ve 23.8.0'dan beri). Testimizde NODE_EXTRA_CA_CERTS ilk durumu çözdü ama UNABLE_TO_VERIFY_LEAF_SIGNATURE hatasını çözmedi: orada sunucu ara sertifikasını göndermiyor ve bunu yalnız sunucunun sahibi düzeltebilir. npm, Git, Python ve curl için çözümler Unable to Get Local Issuer Certificate yazımızda.

Nedeni söyleyen ve yeniden deneyen bir fetch sarmalayıcısı

Betik global fetch'i kullanır, bu yüzden paket kurmanız gerekmez. Hata zincirinin tamamını gezer, her denemeyi AbortSignal.timeout() ile sınırlar ve yalnız sıfırlanan ya da kapanan bağlantılarla zaman aşımlarını, üstel bekleme (backoff) ve jitter ile yeniden dener. DNS hataları, retler, sertifika hataları ve reddedilen proxy girişi her seferinde aynı biçimde düştüğü için bunlarda durur. 429 ve 503 yanıtlarında Retry-After süresi kadar bekler.

429, sunucunun sizden yavaşlamanızı istemesidir. Retry-After başlığının ne anlama geldiğini ve bir işin hızının nasıl ayarlanacağını 429 Too Many Requests ve Rate Limit Hatası Nedir? yazımızda anlattık.

js
// fetch-check.mjs: "TypeError: fetch failed" hatasının asıl nedenini söyler, yalnız düzelebilecek olanı yeniden dener.
// Node.js 22, 24 ve 26'da test edildi. İsteğe bağlı 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));

/** Hatanın kendisi ve içine sarılmış her hata, dıştan içe. */
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": her adres için bir hata
  }
  return chain;
}

/** Zincirdeki ilk metin hata kodu, ör. "ECONNRESET". */
const codeOf = (err) => causes(err).map((e) => e.code).find((c) => typeof c === "string") ?? "";

/** Tek satır: ne düştü ve önce neye bakılmalı. */
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}`;
}

/** Kesin süre sınırı, ağ kesintilerinde bekleme ve 429/503'te Retry-After ile GET. */
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 sn, 1 sn, 2 sn ... artı 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; // yalnız saniye biçimi
      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)}`);
  }
}

Çıktı nasıl görünüyor?

Betiği Node.js 26.10.0'da yanlış yazılmış bir alan adına, localhost üzerinde kapalı bir porta ve her bağlantıyı sıfırlayan (8902), hiç cevap vermeyen (8901), kendi test sertifika otoritemizin sertifikasını kullanan (8906) ve başarıdan önce iki kez 429 dönen (8910) yerel sunuculara karşı çalıştırdık:

text
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 200

Ardından NODE_USE_ENV_PROXY=1 ile yerel test proxy'si üzerinden üç deneme: doğru şifre, yanlış şifre ve proxy adresinin önünde https://:

text
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 ve 24.21.0, rastgele bekleme süreleri dışında aynı satırları yazdırdı.

Bu hatayla nerede karşılaşırsınız?

  • Next.js ve sunucu tarafında çalışan diğer çatılarda: hata sayfaları çoğu zaman yalnız mesajı gösterir.
  • MCP sunucularında ve yapay zekâ ajanı araçlarında: bunlar çoğu zaman bir VPN ya da şirket proxy'si arkasında çalışır.
  • fetch üzerine kurulu SDK'larda: bazı istemcilerin eski sürümleri nedeni hiç aktarmıyordu.
  • CI sunucularında ve Docker derlemelerinde: konteynerlerde çoğu zaman makinenin proxy değişkenleri ve şirketin kök sertifikası yoktur.
  • Scraping ve izleme işlerinde: uzun süren çalışmalar tablodaki kodların çoğuyla er geç karşılaşır.

Yukarıdaki sarmalayıcıdan en çok uzun süren veri toplama işleri yararlanır. Böyle bir iş az sayıda bilinen hedefle çalışıyor ve haftalarca değişmeyen çıkış IP'lerine ihtiyaç duyuyorsa Datacenter Proxy, size ayrılmış IPv4 adreslerini trafik sınırı olmadan verir.

Sık yapılan hatalar

  • err.message yazdırmak. Her zaman fetch failed der.
  • fetch'in HTTPS_PROXY okumasını beklemek. NODE_USE_ENV_PROXY=1 ya da --use-env-proxy olmadan okumaz.
  • npm'deki undici agent'ını global fetch'e vermek. fetch'i aynı paketten alın.
  • Sertifika doğrulamasını kapatmak. NODE_TLS_REJECT_UNAUTHORIZED=0 doğrulamayı bütün süreç için kapatır; araya giren bir saldırgan da şirket proxy'niz gibi görünür.
  • Her şeyi yeniden denemek. Yazım hatası her seferinde aynı biçimde düşer; ECONNRESET sonrası tekrarlanan bir POST iki kez çalışabilir.
  • Süre sınırı koymamak. Takılan bir sunucu isteği beş dakika tutabilir.

Karar rehberi

err.cause içinde gördüğünüzNe yapmalı
ENOTFOUNDAlan adını ya da DNS'i düzeltin; yeniden denemeyin
Proxy adresinizle ECONNREFUSEDProxy adresini ve portunu yeniden kopyalayın
localhost üzerinde ECONNREFUSEDSunucuyu başlatın; portu ve Docker'ı kontrol edin
ECONNRESET, UND_ERR_SOCKETGET isteklerini artan aralıklarla yeniden deneyin
UND_ERR_CONNECT_TIMEOUTGüvenlik duvarına bakın; şirket proxy'si arkasında NODE_USE_ENV_PROXY=1
Uzun bekleme, sonra UND_ERR_HEADERS_TIMEOUTAbortSignal.timeout() ekleyin
Bir sertifika koduNODE_EXTRA_CA_CERTS ya da --use-system-ca
Bir kat aşağıda Proxy response (407)user:pass bilgisini ya da IP whitelist'i kontrol edin
invalid onError methodfetch'i agent ile aynı undici paketinden alın

Sıkça sorulan sorular

fetch 404 ya da 500'de neden hata fırlatmıyor?

Çünkü sunucu cevap vermiştir. Ortada bir ağ hatası olmadığı için fetch normal biçimde sonuçlanır; HTTP hata kodunda res.ok değeri false olur. Gövdeyi okumadan önce bunu kontrol edin.

"fetch failed" ile tarayıcıdaki "Failed to fetch" aynı şey mi?

Akraba ama aynı değil. Chrome hatayı TypeError: Failed to fetch ile reddeder ve nedenini sayfaya söylemez; bu yüzden CORS engeli de koddan aynı görünür, nedeni DevTools'taki Network (Ağ) sekmesinde görürsünüz. Node.js ise nedeni cause içine koyar.

Node.js'te fetch için zaman aşımı nasıl ayarlanır?

signal: AbortSignal.timeout(ms) verin. Süre dolunca fetch TimeoutError ile reddedilir ve sınır takılan bir gövdeyi de durdurur.

Node.js fetch HTTP_PROXY ve HTTPS_PROXY değişkenlerini kullanır mı?

Yalnız NODE_USE_ENV_PROXY=1 (22.21.0, 24.0.0 ve sonrası) ya da --use-env-proxy (22.21.0, 24.5.0 ve sonrası) ile. Bunlar olmadan değişkenler hiçbir uyarı vermeden yok sayılır.

Aynı makinede curl çalışırken fetch neden hata veriyor?

Genellikle iki nedenden biri: curl proxy değişkenlerini okur, fetch okumaz; Windows'la gelen sürüm de dahil pek çok curl derlemesi sertifikaları işletim sisteminin deposuna göre doğrular, Node.js ise --use-system-ca verilmedikçe kendi listesini kullanır.

Proxy "TypeError: fetch failed" hatasını çözer mi?

Yalnız sorun ağ yolundaysa, örneğin internete yalnız proxy'nin çıkabildiği bir şirket ağında. Yazım hatasını, sertifika sorununu ya da kapalı bir sunucuyu düzeltmez. Bağlantınızı sıfırlayan ya da size hız sınırı (rate limit) uygulayan bir siteyi dolanmanın yolu da değildir: yavaşlayın, sitenin resmî API'sini kullanın ya da erişim isteyin.

Özet

TypeError: fetch failed bir sarmalayıcıdır: asıl neden err.cause içindedir, bazen bir kat daha aşağıda. Kodu okuyun, söylediği şeyi düzeltin, her çağrıya bir AbortSignal.timeout() verin ve yalnız sıfırlamaları ve zaman aşımlarını yeniden deneyin. Proxy arkasında NODE_USE_ENV_PROXY=1 kullanın, proxy URL'sini http:// ile yazın ve özellikle Node.js 26'ya geçerken fetch ile agent'ı aynı undici paketinden alın. Kodunuz proxy ile çalışmaya hazır olduğunda seçenekleri proxy hizmetlerimiz sayfasında karşılaştırabilirsiniz.

ChatGPT'ye sorClaude'a sor