---
title: "Node.js TypeError: fetch failed Hatası Nasıl Çözülür?"
description: "Node.js TypeError: fetch failed hatasının nedeni err.cause içindedir. Bu alanı okumayı ve DNS, zaman aşımı, sertifika, proxy sorunlarını çözmeyi anlatıyoruz."
url: https://proxynet.io/tr/blog/typeerror-fetch-failed
date: 2026-10-06
author: "Acar Diveroli"
category: "Nasıl Yapılır, Web Scraping"
lang: tr
---

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

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.

> **Not: Kısa cevap**
>
> `TypeError: fetch failed`, Node.js'in yerleşik fetch'inin HTTP yanıtı gelmeden önce yaşanan her hatada fırlattığı hatadır; asıl neden `err.cause` içindedir, bu yüzden `err.message` yerine onu yazdırın. `ENOTFOUND` alan adının çözülemediğini, `ECONNREFUSED` o portta bağlantı kabul eden bir program olmadığını, `ECONNRESET` ya da `UND_ERR_SOCKET` bağlantının koptuğunu, `UND_ERR_CONNECT_TIMEOUT` ise 10 saniye içinde bağlantı kurulamadığını söyler. Proxy arkasında fetch, Node.js `NODE_USE_ENV_PROXY=1` ile çalıştırılmadıkça `HTTPS_PROXY` değişkenini görmez. `invalid onError method`, npm'den kurulan undici agent'ının Node.js'in içindeki undici ile uyuşmadığını gösterir.

## "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ı](https://fetch.spec.whatwg.org/#fetch-method), 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ı](https://github.com/nodejs/undici/blob/main/docs/docs/api/Errors.md) `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 mesaj | Ne oldu | Önce neye bakılır |
|---|---|---|
| `ENOTFOUND` getaddrinfo ENOTFOUND host | Alan adı çözülemiyor | Yazım, DNS, VPN |
| `ECONNREFUSED` connect ECONNREFUSED 127.0.0.1:8999 | O portta dinleyen yok | Servis, port, proxy portu |
| `AggregateError` içinde `ECONNREFUSED` | Aynısı, `localhost`'un her adresi için | Sunucuyu başlatın |
| `UND_ERR_CONNECT_TIMEOUT` Connect Timeout Error | 10 sn içinde TCP bağlantısı yok | Adres, güvenlik duvarı |
| `ECONNRESET` read ECONNRESET | TCP sıfırlamasıyla kesildi | Proxy, güvenlik duvarı; yeniden deneyin |
| `UND_ERR_SOCKET` other side closed | Yanıttan önce kapandı | Sunucu kayıtları; yeniden deneyin |
| `UND_ERR_HEADERS_TIMEOUT` Headers Timeout Error | Bağlandı, başlıklar zamanında gelmedi | Kendi süre sınırınız |
| `UNABLE_TO_GET_ISSUER_CERT_LOCALLY`, `SELF_SIGNED_CERT_IN_CHAIN` | Kök sertifikaya güvenilmiyor | Şirketin kök sertifikası |
| `UNABLE_TO_VERIFY_LEAF_SIGNATURE` | Sunucu ara sertifikayı göndermedi | Sunucunun sertifika zinciri |
| `ERR_SSL_WRONG_VERSION_NUMBER` | Düz HTTP portuna TLS gönderildi | Proxy URL'sindeki `https://` |
| `UND_ERR_INVALID_ARG` invalid onError method | Başka bir undici sürümünün agent'ı | undici'nin kendi `fetch`'i |
| Kod yok: Request was cancelled. | Proxy tüneli reddetti | Proxy 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](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout_static) 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](https://nodejs.org/api/http.html#built-in-proxy-support) 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](https://proxynet.io/tr/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ı](/tr/blog/nodejs-proxy) 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](https://github.com/nodejs/Release) 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 undici | Node.js 22.23.3 | Node.js 24.21.0 | Node.js 26.10.0 |
|---|---|---|---|
| 5.29.0 | çalışıyor | çalışıyor | invalid onError method |
| 6.29.0 | çalışıyor | çalışıyor | invalid onError method |
| 7.30.0 | çalışıyor | çalışıyor | çalışıyor |
| 8.11.2 | invalid onRequestStart method | invalid 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](/tr/blog/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?](/tr/blog/http-429-too-many-requests) 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](https://proxynet.io/tr/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üz | Ne yapmalı |
|---|---|
| `ENOTFOUND` | Alan adını ya da DNS'i düzeltin; yeniden denemeyin |
| Proxy adresinizle `ECONNREFUSED` | Proxy adresini ve portunu yeniden kopyalayın |
| `localhost` üzerinde `ECONNREFUSED` | Sunucuyu başlatın; portu ve Docker'ı kontrol edin |
| `ECONNRESET`, `UND_ERR_SOCKET` | GET isteklerini artan aralıklarla yeniden deneyin |
| `UND_ERR_CONNECT_TIMEOUT` | Güvenlik duvarına bakın; şirket proxy'si arkasında `NODE_USE_ENV_PROXY=1` |
| Uzun bekleme, sonra `UND_ERR_HEADERS_TIMEOUT` | `AbortSignal.timeout()` ekleyin |
| Bir sertifika kodu | `NODE_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 method` | `fetch`'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](/tr/proxy) sayfasında karşılaştırabilirsiniz.
