Web API'leri bir hata durumunu çoğunlukla DOMException nesnesiyle bildirir. Her DOMException'ın hatayı tanımlayan, PascalCase biçiminde bir name değeri (NotAllowedError, AbortError, SecurityError, QuotaExceededError, TimeoutError...), açıklayıcı bir message metni ve eskiden kalma sayısal bir code değeri vardır. Kodunuzda hatayı ayırt etmek için her zaman name özelliğini kullanın. Hataların önemli bir kısmı ise üç ortak kaynaktan gelir: API'nin güvenli bağlam (HTTPS) istemesi, kullanıcı izni gerektirmesi ya da yalnızca kullanıcı etkinliğinden (bir tıklama gibi) hemen sonra çalışması.

Bu yazı serinin hata başvuru sayfasıdır: hata adlarının anlamları, hangi API'nin hangi adı fırlattığına dair örnekler, güvenli bağlamın tam tanımı, geçici ve kalıcı kullanıcı etkinliği farkı ve fetch() çağrısında görülen "Failed to fetch" türü hataların nasıl okunacağı. DOMException ve AbortSignal örnekleri tarayıcıda da aynı çalışan standart API'ler oldukları için Node.js ile çalıştırıldı. Serinin haritası için Web API öğrenme rehberi, CORS hataları için CORS hataları ve çözümleri yazısına bakın.

DOMException nedir?

MDN'e göre DOMException, bir Web API'sinin metodunu çağırırken ya da bir özelliğine erişirken ortaya çıkan olağan dışı durumu (istisnayı) temsil eden arayüzdür; Web API'lerinde hata koşulları bu yolla anlatılır. Nesnenin üç örnek özelliği vardır. name hatanın adıdır. message bu adla ilişkili açıklamadır ve içeriği tarayıcıdan tarayıcıya değişebileceği için koşullarda kullanılmamalıdır. code ise eski hata sabitlerinden birini ya da eşleşme yoksa 0 döndürür; MDN, yeni hataların artık bu alanı kullanmadığını ve bilgiyi name içine koyduğunu belirtir.

DOMException serileştirilebilir bir nesnedir; structuredClone() ile kopyalanabilir ve postMessage() ile worker'lara aktarılabilir. Yapıcısı new DOMException(message, name) biçimindedir: message verilmezse boş dize, name verilmezse 'Error' kullanılır. Ad standart bir hata adıysa code özelliği ona karşılık gelen eski numarayı döndürür. Aşağıdaki örnek bunları ve iki gerçek hata kaynağını gösterir: serileştirilemeyen bir fonksiyonu kopyalamaya çalışan structuredClone() DataCloneError, geçersiz base64 alan atob() InvalidCharacterError fırlatır.

javascript
const e1 = new DOMException('Kamera izni reddedildi', 'NotAllowedError');
console.log(e1.name, e1.message, e1.code); // NotAllowedError Kamera izni reddedildi 0

const e2 = new DOMException('İşlem iptal edildi', 'AbortError');
console.log(e2.name, e2.code); // AbortError 20

const e3 = new DOMException();
console.log(JSON.stringify(e3.name), JSON.stringify(e3.message), e3.code); // "Error" "" 0

const kopya = structuredClone(e1);
console.log(kopya.name, kopya.message, kopya instanceof DOMException);
// NotAllowedError Kamera izni reddedildi true

try {
  structuredClone({ f: () => 1 });
} catch (hata) {
  console.log(hata instanceof DOMException, hata.name, hata.code); // true DataCloneError 25
}

try {
  atob('***');
} catch (hata) {
  console.log(hata.name, hata.code); // InvalidCharacterError 5
}

NotAllowedError için code değerinin 0 olmasının nedeni, bu adın eski bir sayısal sabite sahip olmamasıdır. structuredClone() ayrıntıları için Web Workers ve postMessage yazısına bakabilirsiniz.

Hata adları ve anlamları

Hata adları ve anlamları: NotAllowedError, AbortError, TimeoutError, SecurityError

MDN yaygın hata adlarını listeler ve bazı API'lerin kendi adlarını tanımladığını, listenin bu yüzden tam olmadığını not eder. Aşağıdaki tablo uygulamada en sık karşılaşılanları ve MDN'de geçtikleri tipik yerleri özetler.

AdEski kodAnlamıTipik kaynak
NotAllowedErroryokKullanıcı aracısı ya da platform isteğe bu bağlamda izin vermiyor, kullanıcı izni reddetmiş olabilirgetUserMedia, Clipboard, WebAuthn, izin politikası
AbortError20İşlem iptal edildiAbortController ile iptal edilen fetch, WebAuthn
TimeoutError23İşlem zaman aşımına uğradıAbortSignal.timeout()
SecurityError18İşlem güvensizGeçersiz alan adı (credentials.get), devre dışı medya desteği
QuotaExceededError22Kota aşıldılocalStorage.setItem, depolama API'leri
NotFoundError8Nesne bulunamadıİstenen türde medya izi yok
NotReadableErroryokGiriş/çıkış okuma işlemi başarısızKamera izin verildiği halde donanım hatası
NotSupportedError9İşlem desteklenmiyorDesteklenmeyen seçenek veya tür
InvalidStateError11Nesne geçersiz durumdaTam etkin olmayan belgede Permissions.query, getUserMedia
DataCloneError25Nesne kopyalanamıyorstructuredClone, postMessage
InvalidCharacterError5Dize geçersiz karakter içeriyoratob()
HierarchyRequestError3Düğüm ağacı hiyerarşisi yanlışDOM ekleme metotları
SyntaxError12Dize beklenen kalıba uymuyorGeçersiz CSS seçici ile querySelector
ConstraintError, VersionError, TransactionInactiveError, ReadOnlyErroryokIndexedDB işlem ve sürüm hatalarıIndexedDB
NetworkError19Ağ hatası oluştuBazı API'ler (ör. FedCM kimlik isteği)

Tablodaki "eski kod" sütunu yalnızca geriye dönük uyumluluk içindir; yeni kod yazarken bu sayılara değil adlara bakın. QuotaExceededError için MDN ayrıca bunun artık DOMException'dan türeyen ayrı bir arayüz olduğunu, bu arayüzü uygulayan tarayıcılarda quota ve requested özellikleriyle ek bilgi taşıyabildiğini, daha eski sürümlerde ise düz bir DOMException olduğunu belirtir; ayrıntı ve destek için MDN uyumluluk tablosuna bakın. TypeMismatchError ise kullanımdan kalkmıştır; yerine JavaScript'in TypeError hatası fırlatılır. Depolama kotası konusu Storage API ve depolama kotası, IndexedDB hataları IndexedDB kullanımı yazısında.

İptal ve zaman aşımı: AbortError ve TimeoutError

İki hata adı kendi kodunuzdan da gelir. AbortController.abort() nedeni (reason) verilmeden çağrıldığında sinyalin reason değeri AbortError adlı bir DOMException olur; bir neden verilirse bu değer olduğu gibi kullanılır. MDN'in credentials.get() sayfası da, abort() çağrısına bir neden geçilirse işlemin AbortError yerine o değerle reddedileceğini not eder. AbortSignal.timeout() ile oluşturulan sinyal ise süre dolduğunda TimeoutError adlı bir DOMException ile iptal olur.

javascript
const kontrol = new AbortController();
kontrol.abort();
console.log(kontrol.signal.aborted, kontrol.signal.reason.name); // true AbortError

const ozel = new AbortController();
ozel.abort(new Error('kullanıcı vazgeçti'));
console.log(ozel.signal.reason.name, ozel.signal.reason.message); // Error kullanıcı vazgeçti

const sure = AbortSignal.timeout(10);
await new Promise((coz) => setTimeout(coz, 50));
console.log(sure.aborted, sure.reason.name, sure.reason instanceof DOMException);
// true TimeoutError true

try {
  kontrol.signal.throwIfAborted();
} catch (hata) {
  console.log(hata.name); // AbortError
}

Bu ayrım kullanıcı arayüzü için önemlidir: AbortError genellikle kullanıcının ya da kodunuzun bilinçli bir iptalidir ve hata mesajı göstermeyi gerektirmez, TimeoutError ise "tekrar deneyin" gibi bir yanıtı hak eder. MDN, AbortSignal.timeout() için zamanlayıcıyı iptal etmenin bir yolu olmadığını ve işlem erken bitse bile zaman aşımının sürdüğünü de belirtir. Fetch ile zaman aşımı kullanımı Fetch API kullanımı yazısında.

"Failed to fetch" ve fetch() hataları

fetch() iki farklı biçimde başarısız olur ve bunları karıştırmak en sık hatadır. MDN'e göre sunucu 404 gibi bir hata koduyla yanıt verdiğinde fetch() reddedilmez, bir Response ile yerine getirilir; bu yüzden response.ok ya da response.status değerini kendiniz kontrol etmeniz gerekir. Promise yalnızca ağ hatası ya da hatalı şema gibi durumlarda reddedilir. Bu durumda gelen hata çoğunlukla bir TypeError'dır; MDN'in fetch() sayfası geçersiz URL, kimlik bilgisi içeren URL, geçersiz seçenek değerleri, izin politikası engeli ve bağlantı yokluğu gibi ağ hatalarını TypeError nedenleri olarak sayar. AbortController ile iptal edilirse AbortError, izin politikasıyla engellenen bazı özelliklerde NotAllowedError gelir.

"Failed to fetch" bir hata adı değil, bu TypeError'ın mesaj metnidir. MDN'in Request.cache örneği, Chrome'un bu durumda mesajı "Failed to fetch" olan bir TypeError fırlattığını gösterir; diğer tarayıcılarda metin farklıdır. Bu yüzden koşulunuzu mesaja değil, hatanın türüne dayandırın. CORS reddi de koda yalnızca bir hata olarak ulaşır: MDN, güvenlik nedeniyle CORS isteğinde neyin yanlış gittiğinin JavaScript'e bildirilmediğini, ayrıntının yalnızca tarayıcı konsolunda görülebildiğini vurgular. Konsoldaki mesajların tek tek açıklaması CORS hataları yazısında, HTTP durum kodları HTTP durum kodları yazısında, Node.js tarafındaki fetch hataları ise Node.js fetch yazısında.

javascript
async function veriGetir(url) {
  try {
    const yanit = await fetch(url, { signal: AbortSignal.timeout(8000) });
    if (!yanit.ok) {
      throw new Error(`Sunucu hatası: ${yanit.status}`); // 404, 500 gibi yanıtlar buraya düşer
    }
    return await yanit.json();
  } catch (hata) {
    if (hata.name === 'TimeoutError') {
      // süre doldu
    } else if (hata.name === 'AbortError') {
      // başka bir yerde iptal edildi
    } else if (hata instanceof TypeError) {
      // ağ hatası, CORS reddi veya geçersiz istek; ayrıntı konsolda
    }
    throw hata;
  }
}

Karma içerik de bu belirtiyle karşınıza çıkabilir: HTTPS bir sayfadan http:// adresine yapılan istekler engellenir. Çözüm için karma içerik hatası yazısına bakın.

Güvenli bağlam (secure context)

Güvenli bağlam (secure context): https://example.com, http://localhost, file:///yol/sayfa.html, http://example.com

Güvenli bağlam, belirli kimlik doğrulama ve gizlilik standartlarını karşılayan bir Window ya da Worker ortamıdır. MDN, bazı API'lerin kullanıcının gizliliğini ihlal etme, bilgisayarına düşük seviyeli erişim ya da kimlik bilgileri gibi verilere ulaşma gücü verdiğini, bunlar güvenli olmayan bağlantıyla sunulan içerikte kullanılabilseydi araya giren bir saldırganın (MITM) da bunlara erişebileceğini açıklar. Bu yüzden Service Worker, Web Crypto, Web Authentication, Clipboard, Geolocation, Notifications, Push, Storage API, Cookie Store API ve daha birçok API yalnızca güvenli bağlamda kullanılabilir; getUserMedia() gibi bazı metotlar da API'nin geri kalanı öyle olmasa bile güvenli bağlam ister.

Kabaca, kaynakları HTTPS üzerinden ya da yerel geri döngü (loopback) adresinden gelen belgeler güvenli bağlamdır. Çerçevedeki belgeler için ek bir koşul vardır: kendileri güvenilir bir URL'den gelmeli ve güvenli bir bağlamın içine gömülmüş olmalıdır. Worker'lar onları oluşturan bağlam güvenliyse güvenlidir; service worker yalnızca güvenli bağlamlardan kaydedilebildiği için her zaman güvenlidir.

AdresGüvenli bağlam mı?
https://example.comEvet (https)
http://localhostEvet (localhost)
file:///yol/sayfa.htmlEvet (file)
http://example.comHayır (http)
https iframe, http üst belge içindeHayır (üst bağlam güvenli değil)
http iframe, https üst belge içindeHayır (iframe adresi güvenli değil)

MDN'e göre potansiyel olarak güvenilir kökenler şunlardır: şeması https, wss veya file olanlar; ana makinesi 127.0.0.0/8 ya da ::1/128 aralığında olanlar; ana makinesi localhost veya .localhost ile bitenler ve tarayıcının kimliği doğrulanmış saydığı şemalar (örneğin tarayıcı eklentilerinin kullandıkları). Bu yüzden geliştirme sırasında http://localhost üzerinde çalışan bir özellik, yerel ağdaki http://192.168.1.20 adresinden açıldığında güvenli bağlam olmadığı için çalışmayabilir. Kodda kontrol için window.isSecureContext veya worker içinde WorkerGlobalScope.isSecureContext kullanılır.

javascript
if (window.isSecureContext) {
  // güvenli bağlam: service worker kaydı yapılabilir
  navigator.serviceWorker.register('/offline-worker.js');
} else {
  // HTTP üzerinden açılmış: güvenli bağlam gerektiren özellikleri gizle
}

Güvenli olmayan bağlamda API'ler her zaman hata fırlatmaz; bazen hiç bulunmaz. MDN'in getUserMedia() sayfası, güvensiz bağlamda navigator.mediaDevices değerinin undefined olduğunu, bu yüzden çağrının TypeError ile sonuçlanabileceğini belirtir. Aynı sayfa, güvensiz bağlamda cihazlar kullanılamıyorsa NotAllowedError geldiğini de söyler. Yani "undefined" ya da "is not a function" türü bir hata gördüğünüzde ilk kontrol, sayfanın HTTPS ile açılıp açılmadığıdır. HTTPS kurulumu için HTTPS ve TLS rehberi yazısına bakın.

İzinler ve Permissions API

Kamera, mikrofon, konum ve bildirim gibi güçlü özellikler kullanıcının iznini ister. Kullanıcı izni reddettiğinde ya da tarayıcı bu bağlamda izin vermediğinde API'ler genellikle NotAllowedError fırlatır. İzin durumunu önceden öğrenmek için navigator.permissions.query() kullanılabilir; dönen PermissionStatus nesnesinin state değeri 'granted', 'denied' veya 'prompt' olur. MDN, 'prompt' durumunun pratikte reddedilmişle aynı olduğunu, özellik kullanılmaya çalışılınca kullanıcıya sorulacağını ya da erişimin reddedileceğini açıklar. Tarayıcının desteklemediği bir izin adı sorgulanırsa promise TypeError ile reddedilir; desteklenen adlar için MDN uyumluluk tablosuna bakın.

javascript
const durum = await navigator.permissions.query({ name: 'geolocation' });
if (durum.state === 'granted') {
  konumluIcerigiGoster();
} else if (durum.state === 'prompt') {
  izinIsteyenDugmeyiGoster();
}
durum.addEventListener('change', () => {
  // kullanıcı izni tarayıcı ayarlarından değiştirdi
});

İzinlerin bir diğer katmanı Permissions-Policy başlığı ve iframe'lerdeki allow özniteliğidir. Bir özellik politika tarafından engellenmişse kullanıcı ne seçerse seçsin çağrı NotAllowedError ile reddedilir; MDN bunu getUserMedia, WebAuthn ve credentials sayfalarında ayrı ayrı belirtir. Kamera ve bildirim izinlerinin ayrıntıları getUserMedia ve Notifications API yazılarında.

Kullanıcı etkinliği: geçici ve kalıcı

Kullanıcı etkinliği: geçici ve kalıcı: Geçici (transient) etkinlik, Kalıcı (sticky) etkinlik

Bazı API'ler, kötüye kullanımı önlemek için yalnızca kullanıcı sayfayla etkileşim halindeyken ya da en az bir kez etkileşime girdiyse çalışır. MDN'e göre etkinliği tetikleyen girdi olayı, isTrusted değeri true olan ve şu türlerden biri olan olaydır: keydown (Esc, tarayıcıya ayrılmış kısayollar ve Caps Lock gibi bazı tuşlar hariç), mousedown, pointerType değeri fare olan pointerdown, fare olmayan pointerup ve touchend. Kodla tetiklenen click() gibi sahte olaylar isTrusted taşımadığı için etkinlik sağlamaz.

TürSüresiGerektiren API örnekleri
Geçici (transient) etkinlikKısa bir zaman aşımıyla biter, bazı API'ler onu tüketirClipboard.writeText ve readText, Window.open, requestFullscreen, showOpenFilePicker, Navigator.share, getDisplayMedia
Kalıcı (sticky) etkinlikOturum sonuna kadar sürerbeforeunload olayı, Navigator.vibrate, medya otomatik oynatma

Pratik sonuç şudur: geçici etkinlik gerektiren bir API'yi sayfa yüklenirken ya da uzun bir await zincirinin sonunda çağırırsanız etkinlik süresi dolmuş olabilir ve çağrı reddedilir. Bu çağrıları doğrudan bir tıklama işleyicisinin içinde, mümkünse ilk adımlarda yapın. Durumu kodla öğrenmek için navigator.userActivation nesnesinin hasBeenActive (kalıcı) ve isActive (geçici) özellikleri vardır; destek için MDN uyumluluk tablosuna bakın. Pano örnekleri Clipboard API yazısında.

javascript
const dugme = document.querySelector('#kopyala');
dugme.addEventListener('click', async () => {
  // tıklamanın sağladığı geçici etkinlik içinde çağrılıyor
  try {
    await navigator.clipboard.writeText('kopyalanacak metin');
  } catch (hata) {
    if (hata.name === 'NotAllowedError') {
      // izin yok ya da etkinlik süresi dolmuş
    }
  }
});

Örnek: hataları kullanıcı mesajına çevirmek

Uygulamanın her yerinde aynı ayrımı tekrar yazmak yerine hata adını tek bir yerde kullanıcıya gösterilecek metne çeviren bir yardımcı işinizi kolaylaştırır. Aşağıdaki fonksiyon DOMException adlarına göre mesaj seçer, AbortError için mesaj göstermez, TypeError'ı ağ ya da istek hatası olarak ele alır ve tanımadığı her şeyi yeniden fırlatır. Örnekteki hatalar elle oluşturuldu; kod tarayıcıda da Node.js'te de aynı çıktıyı verir.

javascript
function kullaniciMesaji(hata) {
  if (hata instanceof DOMException) {
    switch (hata.name) {
      case 'NotAllowedError': return 'İzin verilmedi veya işlem bu bağlamda yapılamaz.';
      case 'SecurityError': return 'Güvenlik kısıtı: sayfa HTTPS üzerinden mi açılıyor?';
      case 'AbortError': return null; // kullanıcı ya da kod iptal etti, mesaj gösterme
      case 'TimeoutError': return 'İşlem zaman aşımına uğradı, tekrar deneyin.';
      case 'QuotaExceededError': return 'Depolama alanı doldu.';
      case 'NotFoundError': return 'İstenen öğe ya da cihaz bulunamadı.';
      case 'NotSupportedError': return 'Bu tarayıcı bu işlemi desteklemiyor.';
      case 'InvalidStateError': return 'Nesne bu işlem için uygun durumda değil.';
      default: return 'Beklenmeyen hata: ' + hata.name;
    }
  }
  if (hata instanceof TypeError) return 'Ağ hatası ya da geçersiz istek.';
  throw hata;
}

const ornekler = [
  new DOMException('', 'NotAllowedError'),
  new DOMException('', 'AbortError'),
  new DOMException('', 'TimeoutError'),
  new DOMException('', 'DataCloneError'),
  new TypeError('Failed to fetch'),
];
for (const h of ornekler) console.log(JSON.stringify(kullaniciMesaji(h)));
// "İzin verilmedi veya işlem bu bağlamda yapılamaz."
// null
// "İşlem zaman aşımına uğradı, tekrar deneyin."
// "Beklenmeyen hata: DataCloneError"
// "Ağ hatası ya da geçersiz istek."

Sık yapılan hatalar

  • Hata koşulunu message metnine bağlamak. "Failed to fetch" gibi metinler tarayıcıya göre değişir.
  • Eski code sayılarına göre karar vermek. Yeni hata adlarının çoğunun kodu yoktur ve 0 döner.
  • 404'ü catch bloğunda beklemek. fetch() HTTP hata kodlarında reddedilmez; response.ok kontrol edin.
  • Yerel ağ IP'si üzerinden HTTP ile test etmek. localhost dışında HTTP adresleri güvenli bağlam değildir.
  • HTTP sayfaya HTTPS iframe gömüp çalışmasını beklemek. Üst bağlam güvenli değilse çerçeve de değildir.
  • Pano veya tam ekran çağrısını tıklamadan uzakta yapmak. Geçici etkinlik süresi dolmuş ya da tüketilmiş olabilir.
  • AbortError'ı kullanıcıya hata olarak göstermek. Çoğu zaman bilinçli bir iptaldir.

Sık Sorulan Sorular

DOMException ile JavaScript Error arasındaki fark nedir?

TypeError, RangeError gibi hatalar JavaScript dilinin hata türleridir; DOMException ise Web API'lerinin tanımladığı hata arayüzüdür. Bir Web API bazı durumlarda TypeError (geçersiz argüman, ağ hatası), bazılarında DOMException fırlatır; MDN sayfalarındaki Exceptions bölümü hangisinin geldiğini söyler. Dil tarafındaki hata türleri için JavaScript öğrenme rehberi serisine bakın.

localhost'ta çalışan özellik sunucuda neden çalışmıyor?

http://localhost güvenli bağlam sayılır, ama sunucunuz http:// ile yayın yapıyorsa güvenli bağlam değildir. Siteyi HTTPS'e taşıyın ve sayfadaki tüm kaynakların HTTPS ile yüklendiğinden emin olun.

NotAllowedError aldım ama kullanıcı izin vermişti, neden?

MDN'e göre aynı ad izin politikası engeli, çapraz kökenli iframe'de eksik allow izni, geçici kullanıcı etkinliğinin olmaması ve güvensiz bağlam gibi durumlarda da kullanılır. İlgili API sayfasının Exceptions bölümündeki maddeleri tek tek kontrol edin.

Kendi kodumda DOMException fırlatmalı mıyım?

Yapıcı herkese açıktır ve MDN örneği de özel bir DOMException fırlatır. Web API'lerine benzeyen bir kütüphane yazıyorsanız iptal için AbortError, zaman aşımı için TimeoutError kullanmak, çağıranların bu durumları tanıdık adlarla ayırt etmesini sağlar.

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: Web API öğrenme rehberi · CORS hataları ve çözümleri · Fetch API kullanımı · HTTPS ve TLS rehberi · Karma içerik hatası · Clipboard API · getUserMedia ile kamera erişimi