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)
Uncaught: gak adatry/catchyang menangkap error ini, jadi kodenya berhenti.TypeError: jenis error-nya. Di sini: operasi dipakai ke nilai yang tipenya salah.Cannot read properties of undefined (reading 'name'): pesannya. Kode mencoba membaca propertinamedari sesuatu yangundefined. Yangundefinedadalah nilai sebelum.name, yaituuser.app.js:9:15: file, baris 9, kolom 15. Kolom 15 jatuh tepat dinamepadareturn user.name.toUpperCase();.
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 [];
}
}
throw new Error(...)melempar error buatanmu sendiri, dengan pesan yang jelas.catch (error)menangkap error apa pun dari bloktry, termasuk yang dilemparfetchatauresponse.json().error.messageberisi teks pesannya saja, tanpa jenis dan stack trace.
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
- Membaca dari bawah dulu. Baris paling atas adalah tempat kode berhenti. Mulai dari sana, lalu turun untuk tahu siapa pemanggilnya.
- Salah menunjuk yang
undefined. Di(reading 'name'), yangundefinedbukanname, tapi nilai di depannya. - Memperbaiki di baris yang error saja.
user?.namemembungkam error-nya, tapi user yang gak ketemu tetap masalah. Perbaiki di sumbernya, misalnya tampilkan "User tidak ditemukan" kalaucariUsermengembalikanundefined. - Di latihan: urutan pengecekan. Cek tipe (
typeof) sebelum cek nol, dan tulis pesannya persis:"Not a number"dan"Division by zero".
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").