Membaca Error Messages — Debugging

Cara Membaca Error Messages Kemampuan membaca error message dengan benar adalah skill debugging paling fundamental. Error message memberitahu kamu apa yang…

Error message adalah laporan browser saat kode berhenti karena masalah: jenis masalahnya, alasannya, di mana kejadiannya, dan jalur pemanggilan yang menuju ke sana. Mirip surat gagal kirim dari kurir: "Gagal antar (jenis), alamat tidak ditemukan (alasan), Jl. Melati 12 (lokasi), rute gudang Jakarta, transit Bandung, kurir Dago (jalur)". Kalau kamu bisa membaca keempatnya, kebanyakan bug ketemu tanpa menebak-nebak.

🧪 Tebakan cepat: sebuah error punya tiga baris at ... di bawahnya. Penyebab bug-nya pasti ada di salah satu fungsi yang tertulis itu, atau bisa juga di fungsi yang gak tertulis sama sekali? Tebak dulu, jawabannya ada di bagian "Stack trace: dibaca dari atas".

Anatomi satu error

Ini app.js kecil: tombol "Lihat profil" menampilkan nama user dengan huruf besar.

const respons = '[{"id":1,"name":"Budi"}]';
const users = JSON.parse(respons);

function cariUser(id) {
  return users.find((u) => u.id === id);
}

function tampilkanNama(user) {
  return user.name.toUpperCase();
}

function tampilkanProfil(id) {
  const user = cariUser(id);
  return tampilkanNama(user);
}

const tombol = document.querySelector("#lihat");
tombol.addEventListener("click", () => {
  console.log(tampilkanProfil(2));
});

Tombolnya diklik, dan Console di Chrome menampilkan ini dengan huruf merah:

Uncaught TypeError: Cannot read properties of undefined (reading 'name')
    at tampilkanNama (app.js:9:15)
    at tampilkanProfil (app.js:14:10)
    at HTMLButtonElement.<anonymous> (app.js:19:15)

Stack trace: dibaca dari atas

Baris-baris at ... disebut stack trace: daftar fungsi yang sedang berjalan saat error terjadi, dari yang terakhir dipanggil.

Baris Artinya
at tampilkanNama (app.js:9:15) di sinilah kode berhenti
at tampilkanProfil (app.js:14:10) yang memanggil tampilkanNama, di baris 14
at HTMLButtonElement.<anonymous> (app.js:19:15) awal semuanya: fungsi tanpa nama yang jalan sebagai listener klik tombol

Klik app.js:9:15 di Console, dan panel Sources langsung terbuka di baris itu. Kalau trace-nya panjang dan banyak baris dari library (misalnya react-dom), cari baris pertama yang menunjuk ke file kamu.

Tapi lokasi berhenti belum tentu lokasi penyebab. user bernilai undefined karena cariUser(2) gak menemukan user dengan id 2, dan find mengembalikan undefined. cariUser sudah selesai jalan sebelum error terjadi, jadi namanya gak ada di stack trace. Itu jawaban tebakannya: penyebabnya bisa ada di fungsi yang gak tertulis. Stack trace memberi tahu di mana kode berhenti; kamu tetap harus bertanya dari mana nilai yang salah itu datang.

Empat jenis error yang paling sering

Jenis Contoh pesan (persis seperti Chrome) Artinya, cek ini dulu
TypeError Cannot read properties of undefined (reading 'name') nilai sebelum .name itu undefined: data belum datang, atau pencarian gak ketemu
TypeError users.findd is not a function yang dipanggil bukan fungsi: typo nama method, atau nilainya bukan tipe yang kamu kira
TypeError Cannot set properties of null (setting 'textContent') querySelector gak menemukan elemennya, hasilnya null
ReferenceError usr is not defined gak ada variabel bernama itu di scope mana pun yang terjangkau: typo, atau dideklarasikan di fungsi lain
ReferenceError Cannot access 'total' before initialization let/const dipakai sebelum barisnya jalan
SyntaxError Unexpected token '<', "<!DOCTYPE "... is not valid JSON JSON.parse menerima halaman HTML, biasanya halaman 404 dari URL API yang salah
SyntaxError Unexpected end of input ada kurung kurawal atau kurung yang belum ditutup
RangeError Maximum call stack size exceeded fungsi rekursif yang gak pernah berhenti

Satu catatan: SyntaxError dari kode yang salah tulis muncul waktu file dimuat, sebelum satu baris pun jalan. SyntaxError dari JSON.parse muncul waktu baris itu jalan, dan trace-nya diawali at JSON.parse (<anonymous>), fungsi bawaan browser. Baris berikutnya yang menunjuk ke kode kamu.

Error yang gak tertangkap di level paling atas menghentikan sisa script. Kalau JSON.parse di baris 2 gagal, baris 17 sampai 20 gak pernah jalan, jadi tombolnya diam saja waktu diklik.

Error di kode async

Error di dalam fungsi async yang gak ditangkap ditulis Chrome dengan awalan Uncaught (in promise):

Uncaught (in promise) SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON

Tangkap dengan try/catch di sekitar await, dan cek response.ok sebelum membaca JSON:

async function muatUsers() {
  try {
    const response = await fetch("/api/users");
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    return await response.json();
  } catch (error) {
    console.error("Gagal memuat users:", error.message);
    return [];
  }
}

Latihan: mengelompokkan error dan melempar error sendiri

Di latihan ada dua function. classifyError(errorMessage) menebak jenis error dari potongan teks di pesannya, dicek berurutan dan langsung return begitu ada yang cocok. safeDivide(a, b) membagi, tapi lebih dulu menolak masukan yang gak masuk akal dengan throw.

function safeDivide(a, b) {
  if (typeof a !== "number" || typeof b !== "number") throw new Error("Not a number");
  if (b === 0) throw new Error("Division by zero");
  return a / b;
}
Panggilan Hasil yang dicetak
classifyError("x is not defined") ReferenceError
classifyError("foo is not a function") TypeError:NotFunction
classifyError("Cannot read properties of null") TypeError:NullAccess
classifyError("Unexpected token }") SyntaxError
classifyError("Something went wrong") Unknown
safeDivide(10, 2) 5
safeDivide(10, 0) Division by zero (dari e.message di catch)
safeDivide("abc", 2) Not a number

Tanpa penjaga, 10 / 0 di JavaScript gak error, hasilnya Infinity, dan "abc" / 2 hasilnya NaN. Dua nilai itu menyebar diam-diam ke perhitungan berikutnya. throw menghentikannya di tempat, dengan pesan yang menjelaskan alasannya.

🎯 Stack trace di production

Di production, kode biasanya di-minify: nama fungsi dipendekkan dan semuanya jadi satu baris panjang. Trace-nya jadi seperti ini:

Uncaught TypeError: Cannot read properties of undefined (reading 'name')
    at q (app.min.js:1:48291)
    at r (app.min.js:1:49012)

q di baris 1 kolom 48.291 gak bisa dibaca manusia. Solusinya source map, file .map yang memetakan posisi itu kembali ke file dan baris aslinya. DevTools membacanya otomatis kalau tersedia; pelajaran Source Maps di jalur ini membahasnya lengkap.

⚠️ Jebakan yang sering ketemu

TL;DR: Error di Console punya empat bagian: jenis (TypeError, ReferenceError, SyntaxError, RangeError), pesan, lokasi file:baris:kolom, dan stack trace. Stack trace dibaca dari atas: baris pertama tempat kode berhenti, di bawahnya para pemanggil, paling bawah awal kejadiannya (misalnya listener klik). Cannot read properties of undefined (reading 'name') berarti nilai sebelum .name itu undefined; x is not a function berarti yang dipanggil bukan fungsi; x is not defined berarti nama itu gak ada di scope; Unexpected token '<' dari JSON.parse berarti server mengirim HTML. Penyebabnya bisa di fungsi yang sudah selesai dan gak tercantum di trace. Error async tertulis Uncaught (in promise); tangkap dengan try/catch di sekitar await, dan lempar error sendiri dengan throw new Error("pesan").

Yang akan kamu pelajari