EADDRINUSE, sunucunuzun dinlemek istediği adres ve portu başka bir sürecin zaten kullandığını söyler; çözüm ya o süreci kapatmak ya da başka bir port seçmektir. ENOENT verilen yolda dosya veya klasör olmadığını, EACCES dosya izinlerinin işlemi engellediğini, ECONNREFUSED karşı tarafın bağlantıyı reddettiğini (çoğu zaman orada çalışan bir servis olmadığını), ECONNRESET ise bağlantının karşı taraf tarafından aniden kapatıldığını bildirir. Bu kodların hepsi işletim sisteminden gelir ve Node.js bunları error.code alanında taşıyan sistem hatalarına dönüştürür.

Bu yazıda sistem hatası nesnesinin alanlarını, en sık görülen kodların ne anlama geldiğini ve her biri için gerçek çıktılarla nasıl tepki verebileceğinizi gösteriyoruz. Node.js'in kendi ürettiği ERR_* kodları ayrı bir aileye aittir; onları ERR_MODULE_NOT_FOUND ve hata kodları yazısında anlattık. Örnekler Windows üzerinde Node.js v26.13.0 ile çalıştırıldı; çıktılardaki yerel yolları /home/kullanici/proje olarak kısalttık.

Sistem hatası nesnesinde neler var?

Sistem hatası nesnesinde neler var? code, errno, syscall, path

Belgeler bu hataları SystemError başlığı altında tanımlar: uygulama bir işletim sistemi kısıtını aştığında, örneğin olmayan bir dosyayı okumaya çalıştığında oluşurlar. Bu nesneler de Error örneğidir, ama standart alanlara ek olarak sorunun bağlamını anlatan alanlar taşırlar.

AlanTipiNe anlatır
codestringHata kodu, örneğin ENOENT
errnonumberSistemin verdiği negatif hata numarası; platforma göre değişir
syscallstringBaşarısız olan sistem çağrısının adı: open, listen, connect...
pathstringVarsa ilgili dosya yolu
deststringVarsa dosya işleminin hedef yolu (kopyalama, yeniden adlandırma)
addressstringVarsa bağlantının kurulamadığı adres
portnumberVarsa kullanılamayan port
infoobjectVarsa hata durumuyla ilgili ek ayrıntılar
javascript
// enoent.mjs
import { readFile } from 'node:fs/promises';
import util from 'node:util';

try {
  await readFile(new URL('./ayarlar.json', import.meta.url), 'utf8');
} catch (err) {
  console.log(err.code, err.syscall, err.path);
  console.log(err.message);
  console.log(util.getSystemErrorName(err.errno));
  console.log(util.getSystemErrorMessage(err.errno));
  console.log(err instanceof Error, typeof err.errno);
}
// çıktı: ENOENT open /home/kullanici/proje/ayarlar.json
// çıktı: ENOENT: no such file or directory, open '/home/kullanici/proje/ayarlar.json'
// çıktı: ENOENT
// çıktı: no such file or directory
// çıktı: true number

Mesaj, kodu, açıklamayı, sistem çağrısını ve yolu tek satırda birleştirir. Kodu programatik olarak kontrol etmek için err.code kullanın; mesaj metnine güvenmeyin. errno değerini doğrudan karşılaştırmak da iyi bir fikir değildir: belgeye göre bu sayı libuv hata tablosuna karşılık gelir, Windows'ta sistemin verdiği numara libuv tarafından normalleştirilir ve sayı ile ad arasındaki eşleme platforma bağlıdır. Bir numaranın adını ve açıklamasını öğrenmek için util.getSystemErrorName() ve util.getSystemErrorMessage() fonksiyonlarını kullanabilirsiniz; tüm eşlemeyi util.getSystemErrorMap() bir Map olarak döndürür.

Sistem hatalarının nasıl iletildiği API'nin biçimine bağlıdır. Senkron API'ler (readFileSync gibi) hatayı fırlatır, Promise döndürenler reddeder, callback alanlar ilk argüman olarak verir, net.Server gibi EventEmitter tabanlı nesneler ise 'error' olayı yayar. Son durumda dinleyici eklemezseniz hata yakalanmamış istisnaya dönüşür ve süreç çöker; bu davranışı uncaughtException ve unhandledRejection yazısında ele aldık.

EADDRINUSE: adres zaten kullanımda

Belgede dinleme sırasında en sık görülen hata olarak geçer: başka bir sunucu aynı port, yol veya tanıtıcı üzerinde zaten dinliyordur. Aşağıdaki örnek önce işletim sisteminden boş bir port alıyor (port 0), sonra ikinci bir sunucuyu aynı porta bağlamaya çalışıyor.

javascript
// port.mjs
import net from 'node:net';

const ilk = net.createServer();
ilk.listen(0, '127.0.0.1', () => {
  const { port } = ilk.address();
  const ikinci = net.createServer();
  ikinci.on('error', (err) => {
    console.log(err.code, err.syscall, err.address, err.port === port);
    console.log(err.message.replace(String(port), 'PORT'));
    ilk.close();
  });
  ikinci.listen(port, '127.0.0.1');
});
// çıktı: EADDRINUSE listen 127.0.0.1 true
// çıktı: listen EADDRINUSE: address already in use 127.0.0.1:PORT

listen() asenkron çalıştığı için hata fırlatılmaz, sunucu nesnesinde 'error' olayı olarak gelir; try/catch ile yakalayamazsınız. Hata nesnesindeki address ve port alanları hangi adresin dolu olduğunu kesin olarak söyler. Bu hatayı gördüğünüzde kontrol edeceğiniz şeyler genellikle şunlardır: aynı uygulamanın önceki bir kopyası hâlâ çalışıyor mu, yoksa aynı portu kullanan başka bir servis mi var.

Belge, portu kullanan süreç kısa süre içinde kapanacaksa bir süre bekleyip yeniden denemeyi örnek olarak gösteriyor: 'error' dinleyicisinde kod EADDRINUSE ise setTimeout ile bir saniye sonra server.close() ve ardından yeniden server.listen() çağrılıyor. Belgeye göre listen() yalnızca ilk çağrı hatayla sonuçlandıysa ya da close() çağrıldıysa yeniden çağrılabilir; aksi hâlde ERR_SERVER_ALREADY_LISTEN alırsınız. Belgeye göre port 0 verilirse ya da hiç verilmezse işletim sistemi kullanılmayan bir port atar ve bu port server.address().port ile okunur; testlerde sabit port yerine bunu kullanmak çakışmaları önler. Birden fazla sürecin aynı portu paylaşması ise cluster konusudur; Node.js cluster ve PM2 yazısında anlatılıyor.

ECONNREFUSED: bağlantı reddedildi

Bu kod, hedef makinenin bağlantıyı etkin olarak reddettiği anlamına gelir; belgeye göre genellikle karşı tarafta o portta çalışan bir servis olmamasından kaynaklanır. Aşağıdaki örnek bir portu açıp hemen kapatıyor, sonra o porta bağlanmaya çalışıyor.

javascript
// baglanti.mjs
import net from 'node:net';

// Önce boş bir port bulup kapatıyoruz; artık o portu dinleyen kimse yok.
const gecici = net.createServer().listen(0, '127.0.0.1');
await new Promise((r) => gecici.once('listening', r));
const { port } = gecici.address();
await new Promise((r) => gecici.close(r));

const soket = net.connect(port, '127.0.0.1');
soket.on('error', (err) => {
  console.log(err.code, err.syscall, err.address);
});
// çıktı: ECONNREFUSED connect 127.0.0.1

Aynı durumu yerleşik fetch() ile denediğinizde tablo değişir. fetch sistem hatasını doğrudan fırlatmaz; mesajı "fetch failed" olan bir TypeError ile reddeder ve asıl sistem hatasını cause alanına koyar. Bu yüzden err.code kontrolü fetch için boş döner, doğru kontrol err.cause?.code şeklindedir.

javascript
// fetchhata.mjs (port hazırlığı baglanti.mjs ile aynı)
try {
  await fetch(`http://127.0.0.1:${port}/`);
} catch (err) {
  console.log(err.name, '|', err.message);
  console.log('cause:', err.cause?.code);
}
// çıktı: TypeError | fetch failed
// çıktı: cause: ECONNREFUSED

Geliştirme ortamında ECONNREFUSED aldığınızda ilk bakılacak yer, bağlanmak istediğiniz servisin (veritabanı, başka bir API) gerçekten çalışıp çalışmadığı ve doğru adres ile portta dinleyip dinlemediğidir. fetch ile istek gönderme ayrıntıları fetch ile HTTP isteği, TCP bağlantıları net modülü ile TCP sunucu yazısında.

ECONNRESET: bağlantı karşı taraftan koparıldı

Belgeye göre bu kod, bağlantının karşı tarafça zorla kapatıldığını gösterir ve genellikle uzak soketin zaman aşımı ya da yeniden başlatma nedeniyle kaybolmasından kaynaklanır. HTTP istemcisinde yanıt gelmeden bağlantı koparsa istek nesnesi "socket hang up" mesajı ve ECONNRESET koduyla bir 'error' olayı yayar.

javascript
// reset.mjs
import http from 'node:http';

const sunucu = http.createServer((req, res) => {
  req.socket.destroy(); // yanıt vermeden bağlantıyı kopar
});
sunucu.listen(0, '127.0.0.1', () => {
  const istek = http.get({ port: sunucu.address().port, host: '127.0.0.1' });
  istek.on('error', (err) => {
    console.log(err.code, '|', err.message);
    sunucu.close();
  });
});
// çıktı: ECONNRESET | socket hang up

HTTP belgesi bu hatanın gizli bir nedenine de dikkat çekiyor: keep-alive kullanan bir istemci, sunucunun tam o anda kapattığı bir soketi yeniden kullanmaya çalışırsa istek ECONNRESET ile başarısız olabilir. Belge bu durumda req.reusedSocket değeri doğruysa isteği yeniden denemeyi gösteriyor. Sunucu tarafında ise server.keepAliveTimeoutBuffer ayarı, iç soket zaman aşımını duyurulan keep-alive süresinin biraz ötesine uzatarak bu hataları azaltmayı amaçlar. 'clientError' dinleyicisinde de belge, sokete yazmadan önce hatanın ECONNRESET olup olmadığını ve soketin hâlâ yazılabilir olduğunu kontrol etmeyi öneriyor. HTTP sunucusu kurmanın temelleri http modülü ile sunucu yazısında.

Dosya sistemi hataları

Dosya sistemi hataları: ENOENT, EEXIST, EISDIR, ENOTDIR

Dosya işlemlerinde karşılaşılan kodlar çoğu zaman tek kelimeyle sorunu anlatır. Aşağıdaki örnek, içinde bir dosya bulunan dolu klasörü üzerinde dört hatalı işlem deniyor ve her birinin kodunu ve sistem çağrısını yazdırıyor.

javascript
// fsler.mjs
import { mkdir, readFile, rmdir, open } from 'node:fs/promises';

const denemeler = {
  EEXIST: () => mkdir('dolu'),
  EISDIR: () => readFile('dolu'),
  ENOTEMPTY: () => rmdir('dolu'),
  wx: () => open('dolu/a.txt', 'wx'),
};
for (const [ad, fn] of Object.entries(denemeler)) {
  try {
    await fn();
    console.log(ad, 'başarılı');
  } catch (err) {
    console.log(ad.padEnd(9), '=>', err.code, err.syscall);
  }
}
// çıktı: EEXIST    => EEXIST mkdir
// çıktı: EISDIR    => EISDIR read
// çıktı: ENOTEMPTY => ENOTEMPTY rmdir
// çıktı: wx        => EEXIST open
KodBelgedeki anlamıTipik neden
ENOENTBöyle bir dosya veya klasör yokYanlış yol, yanlış çalışma dizini, henüz oluşturulmamış dosya
EEXISTHedef zaten varrecursive olmadan mkdir, wx bayrağıyla açma
EISDIRDosya beklenirken klasör verildiBir klasörü readFile ile okumak
ENOTDIRYolun bir parçası klasör değilreaddir için dosya yolu vermek
ENOTEMPTYKlasör boş değilİçi dolu klasörü rmdir ile silmek
EACCESİzin reddedildiDosya izinleri işlemi yasaklıyor
EPERMİşleme izin verilmediYükseltilmiş yetki gerektiren bir işlem
EMFILEÇok fazla açık dosyaAynı anda çok sayıda dosya açmak

readFile bir klasöre uygulandığında davranış belgeye göre platforma bağlıdır: macOS, Linux ve Windows'ta işlem hatayla sonuçlanır, FreeBSD'de ise klasörün içeriğinin bir temsili döner. Örnekteki 'wx' bayrağı dosya varsa açmayı başarısız kılar; bir dosyanın üzerine yanlışlıkla yazmamak için kullanışlıdır ve writeFile ve appendFile yazısında anlatılıyor. mkdir çağrısına recursive: true verirseniz var olan klasör için EEXIST almazsınız; ayrıntılar klasör işlemleri yazısında.

Belge önemli bir desen öneriyor: bir dosyayı açmadan önce fs.access() ile var olup olmadığını kontrol etmeyin. İki çağrı arasında başka bir süreç dosyayı değiştirebilir ya da silebilir; bu bir yarış durumudur. Doğru yol dosyayı doğrudan açmak ve ENOENT hatasını o anda ele almaktır. Bu konuyu fs.stat ve dosya var mı kontrolü yazısında ayrıntılı işledik.

EMFILE: açık dosya sınırı

EMFILE, süreç için izin verilen dosya tanımlayıcısı sayısına ulaşıldığını gösterir; en az bir tanımlayıcı kapatılana kadar yenisi açılamaz. Belgeye göre bu hata özellikle çok sayıda dosya paralel açıldığında ve süreç başına düşük sınırı olan sistemlerde (özellikle macOS) görülür. Belge, düşük bir sınırı yükseltmek için Node.js'i çalıştıracağınız kabukta ulimit -n 2048 komutunu öneriyor. Uzun vadeli çözüm ise aynı anda açık tutulan dosya sayısını sınırlamaktır: binlerce dosyayı tek bir Promise.all ile açmak yerine işleri küçük gruplar hâlinde yapın ve açtığınız FileHandle nesnelerini kapatmayı unutmayın (fs.promises ve FileHandle).

fs.rm ve fsPromises.rm geçici hatalar için yerleşik bir yeniden deneme mekanizması sunar: recursive: true ile birlikte maxRetries verirseniz EBUSY, EMFILE, ENFILE, ENOTEMPTY veya EPERM hatası alındığında işlem, her denemede retryDelay milisaniye daha uzun bekleyerek tekrarlanır.

Diğer sık görülen kodlar

Diğer sık görülen kodlar

EPIPE, okuyan tarafı kalmamış bir boruya, sokete ya da FIFO'ya yazmaya çalışıldığında oluşur; net ve http katmanlarında karşı tarafın kapandığını gösterir. ETIMEDOUT, bağlanma ya da gönderme isteğinin karşı taraf belirli bir süre yanıt vermediği için başarısız olduğunu söyler; belge bunun çoğu zaman socket.end() çağrısının düzgün yapılmadığının işareti olduğunu belirtiyor. ENOTFOUND ise bir DNS çözümleme hatasıdır ve belgenin vurguladığı gibi standart bir POSIX hatası değildir; alan adını yanlış yazdığınızda ya da ağ bağlantınız olmadığında görülür.

Hataları koda göre ele almak

Sistem hatalarıyla çalışırken iyi bir kural şudur: beklediğiniz ve anlamlı bir tepki verebileceğiniz kodları ayrı ayrı ele alın, geri kalanını yeniden fırlatın. Aşağıdaki fonksiyon, ayar dosyası yoksa varsayılan değerleri döndürüyor, yol bir klasöre çıkıyorsa daha açıklayıcı bir hata üretiyor ve diğer tüm hataları olduğu gibi yukarı iletiyor.

javascript
// ayaroku.mjs
import { readFile } from 'node:fs/promises';

async function ayarlariOku(dosya) {
  try {
    return JSON.parse(await readFile(dosya, 'utf8'));
  } catch (err) {
    if (err.code === 'ENOENT') return { port: 3000 }; // dosya yoksa varsayılan
    if (err.code === 'EISDIR') throw new Error(`${dosya} bir klasör`, { cause: err });
    throw err; // EACCES, EMFILE vb. gizlenmesin
  }
}

console.log(await ayarlariOku('yok.json'));
try {
  await ayarlariOku('dolu');
} catch (err) {
  console.log(err.message, '|', err.cause.code);
}
// çıktı: { port: 3000 }
// çıktı: dolu bir klasör | EISDIR

cause seçeneği orijinal sistem hatasını kaybetmeden daha anlaşılır bir mesaj vermenizi sağlar. Belgeye göre util.inspect() bu alanı da yazdırdığı için loglarda iki hata birlikte görünür.

Sık yapılan hatalar

Sunucu hatasını try/catch ile yakalamaya çalışmak. server.listen() çağrısını try bloğuna almak EADDRINUSE hatasını yakalamaz, çünkü hata daha sonra 'error' olayı olarak gelir. Sunucuya 'error' dinleyicisi ekleyin.

fetch hatasında err.code aramak. fetch sistem hatalarını "fetch failed" mesajlı bir TypeError içine sarar. Asıl kod err.cause.code alanındadır.

errno sayısını sabit olarak karşılaştırmak. Bu sayı platforma göre değişir. Karşılaştırmayı her zaman err.code dizesiyle yapın.

Varlık kontrolü ile işlemi ayırmak. Önce access ya da existsSync ile kontrol edip sonra dosyayı açmak bir yarış durumu yaratır. Dosyayı doğrudan açıp ENOENT hatasını ele alın.

Tüm hataları aynı şekilde ele almak. ENOENT ile EACCES aynı sorun değildir; ilkinde varsayılan bir değerle devam etmek mantıklı olabilir, ikincisinde ise bir yapılandırma hatası vardır ve gizlenmemelidir.

Sık Sorulan Sorular

EADDRINUSE alıyorum ama hiçbir şey çalışmıyor gibi görünüyor, neden?

Genellikle aynı uygulamanın önceki bir kopyası arka planda çalışmaya devam ediyordur ya da başka bir servis aynı portu kullanıyordur. Portu hangi sürecin kullandığını işletim sisteminizin ağ araçlarıyla bulun; geliştirme sırasında sabit port yerine ortam değişkeninden gelen bir port kullanmak çakışmaları azaltır.

ECONNREFUSED ile ETIMEDOUT arasındaki fark nedir?

ECONNREFUSED'da karşı makine bağlantıyı etkin olarak reddeder; belgeye göre bu genellikle orada etkin bir servis olmadığını gösterir. ETIMEDOUT'ta ise karşı taraf belirli bir süre boyunca düzgün yanıt vermediği için bağlanma ya da gönderme isteği başarısız olur.

Sistem hata kodlarının tam listesi nerede?

Node.js belgesi yalnızca sık görülenleri listeler ve tam liste için errno(3) kılavuz sayfasını gösterir. Çalışma anında desteklenen kodları util.getSystemErrorMap() ile, platforma özgü sabitleri os.constants.errno ile görebilirsiniz.

Windows ve Linux'ta aynı hata kodlarını mı alırım?

Yaygın kodlar (ENOENT, EADDRINUSE, ECONNREFUSED) iki platformda da aynı adla gelir, ama errno sayıları farklıdır. Bazı işlemlerde kod da değişebilir: denememizde Windows'ta var olan bir dosyanın altına yeni bir dosya yazmaya çalışmak ENOENT ile sonuçlandı. Platforma bağlı davranışlar için ilgili API belgesini kontrol edin ve testlerinizi hedef platformda çalıştırın.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: Node.js öğrenme rehberi · Node.js ERR_* hata kodları · uncaughtException ve unhandledRejection · fs.stat ve dosya var mı kontrolü · net modülü ile TCP sunucu