Galat sudah muncul rapi di layar: ada name, ada message, ada transkripnya. Pekerjaan tidak berhenti di situ — transkrip hanyalah laporan bahwa sesuatu gagal, sedangkan yang kita cari adalah penyebabnya: baris mana yang mengisi data salah, dan kapan itu terjadi. Section ini ditutup dengan tiga alat pelacakannya: membaca stack trace baris demi baris, console.trace untuk melihat jejak pemanggil saat program masih sehat, dan debugger untuk berhenti tepat sebelum baris bermasalah dijalankan.

Membaca Stack Trace Baris demi Baris #

Aturannya: setiap galat yang tidak tertangkap membawa daftar pemanggil yang disebut stack trace — jejak tumpukan function yang sedang berjalan saat kegagalan terjadi. Daftar ini disusun dari lokasi kegagalan di baris paling atas, lalu pemanggilnya, lalu pemanggil dari pemanggilnya, sampai titik masuk program.

Berkas berikut memuat galat yang sengaja belum ditangkap, dengan rantai pemanggil tiga lapis:

// lacak-galat.js
// Transkrip galat untuk latihan membaca stack trace.

function hitungTotal(keranjang) {
  let total = 0;
  for (const item of keranjang) {
    total += item.harga * item.jumlah;
  }
  return total;
}

function muatKeranjang() {
  return [
    { nama: "Kabel USB", harga: 25000, jumlah: 2 },
    { nama: "Buku Catatan", harga: 15000, jumlah: 3 },
  ];
}

function jalankanKasir() {
  const keranjang = muatKeranjang();
  keranjang[1] = undefined; // penyebab: satu entri tidak terbaca
  return "Total: Rp" + hitungTotal(keranjang);
}

console.log(jalankanKasir());

Ketika dijalankan, Node menampilkan transkrip berikut (path dipendekkan; baris internal engine diringkas):

text
/home/…/lacak-galat.js:7
    total += item.harga * item.jumlah;
                  ^

TypeError: Cannot read properties of undefined (reading 'harga')
    at hitungTotal (/home/…/lacak-galat.js:7:19)
    at jalankanKasir (/home/…/lacak-galat.js:22:24)
    at Object.<anonymous> (/home/…/lacak-galat.js:25:13)
    at Module._compile (node:internal/modules/cjs/loader:1929:14)
    ... 6 baris internal engine lainnya ...
Node.js v24.21.0

Mari kita baca stack trace-nya dari atas ke bawah:

  1. Baris atas (at hitungTotal (...:7:19)): lokasi kegagalan sesungguhnya — function hitungTotal, berkas lacak-galat.js, baris 7, kolom 19. Kolom itu menunjuk tepat ke harga dalam item.harga.
  2. Frame kedua (at jalankanKasir (...:22:24)): siapa yang memanggil hitungTotal — baris 22, di mana keranjang[1] sudah diganti undefined. Frame ini langsung menunjuk ke arah penyebab.
  3. Frame ketiga (at Object.<anonymous> (...:25:13)): titik masuk — program utama memanggil jalankanKasir(). Urutan hitungTotal → jalankanKasir → Object.<anonymous> adalah rantai pemanggil lengkapnya.
  4. Frame at Module._compile ... ke bawah: baris-baris internal engine Node. Frame semacam ini bukan kode kita dan dapat diabaikan saat diagnosis.

Pola bacanya konsisten: baris paling atas = di mana program gagal; ke bawah = dari mana lompatan berasal. Pada kasus di atas, kegagalan terjadi di hitungTotal, tetapi data yang bermasalah disiapkan di jalankanKasir baris 21 — galat sering muncul di satu tempat sementara penyebabnya dibuat di tempat lain, dan stack trace adalah yang menghubungkan keduanya.

Satu variasi lain yang perlu dikenali: function rekursif — materi chapter Recursion di section Functions — memunculkan frame berulang untuk tiap kedalaman pemanggilan. Delapan frame dengan nama sama berarti fungsi itu dipanggil bersarang delapan tingkat; frame teratas tetap menunjuk lokasi kegagalan, dan frame terbawah menunjuk pemanggilan pertama.

Kamus Pesan Galat yang Sering Muncul #

Sebagian besar kegagalan harian berputar pada beberapa pesan yang sama. Mengenali polanya mempercepat pencarian:

PesanArtiYang diperiksa lebih dulu
X is not definedNama X tidak pernah dideklarasikanSalah ketik nama, atau variabel di luar scope
X is not a functionNilai yang dipanggil sebagai function ternyata bukan functionApakah nama yang ditulis benar-benar berisi function
Cannot read properties of undefined (reading 'x')Membaca properti x dari undefinedDari mana nilai yang undefined itu berasal
Unexpected token '...'Parser menemukan tanda pada posisi yang tidak sahTanda baca di sekitar posisi yang ditunjuk ^^

Dua pesan pertama menyebut nama yang bermasalah secara langsung, sehingga perbaikannya sering hanya satu baris. Dua pesan berikutnya selalu disertai posisi — baris dan kolom pada galat runtime, tanda ^^ pada galat parsing — yang menunjuk titik pemeriksaan.

console.trace: Jejak Tanpa Galat #

Stack trace biasanya baru muncul setelah kegagalan. Ada saatnya kita ingin melihat jejak pemanggil justru ketika program berjalan dengan baik — misalnya untuk memastikan sebuah fungsi dipanggil dari mana saja. Method console.trace mencetak jejak tumpukan di posisi pemanggilannya tanpa perlu melempar galat:

// jejak-console.js
// console.trace: mencetak jejak pemanggil tanpa perlu galat.

function ongkir(berat) {
  console.trace("posisi: ongkir");
  return berat * 5000;
}

function hitungTagihan() {
  return 25000 + ongkir(2);
}

console.log(hitungTagihan());

Output yang Dihasilkan:

text
Trace: posisi: ongkir
    at ongkir (/home/…/jejak-console.js:5:11)
    at hitungTagihan (/home/…/jejak-console.js:10:18)
    at Object.<anonymous> (/home/…/jejak-console.js:13:13)
    at Module._compile (node:internal/modules/cjs/loader:1929:14)
    ... 6 baris internal engine lainnya ...
35000

Formatnya sama persis dengan stack trace galat — hanya berlabel Trace: dan program berlanjut normal, terbukti dari 35000 yang tercetak setelahnya. Satu catatan teknis: console.trace menulis ke stderr, sedangkan console.log menulis ke stdout; pada terminal keduanya tampak menyatu, tetapi keduanya dapat dipisah saat dialihkan ke berkas. Teks "posisi: ongkir" adalah label bebas untuk menandai jejak ini saat ada beberapa pemanggilan sekaligus.

debugger: Berhenti Sebelum Baris Dijalankan #

Membaca transkrip adalah analisis setelah kejadian. Debugger bekerja dengan cara berlawanan: program dihentikan sebelum baris tertentu dijalankan, sehingga nilai setiap variabel masih bisa diperiksa apa adanya. Node tanpa debugger hanya melaporkan akibat; debugger memperlihatkan sebabnya secara langsung.

Alurnya dengan Node dan Chrome DevTools:

  1. Jalankan berkas dengan node --inspect-brk kasir.js. Opsi --inspect-brk menghentikan program di baris pertama, sebelum apa pun dieksekusi.
  2. Buka Chrome dan kunjungi chrome://inspect, lalu pilih berkas yang sedang menunggu di bawah Remote Target.
  3. Di panel Sources, klik nomor baris untuk memasang breakpoint — penanda baris tempat program harus berhenti — misalnya pada baris total += item.harga * item.jumlah;.
  4. Tekan tombol lanjut; program berjalan dan berhenti tepat di breakpoint.
  5. Periksa panel Scope (nilai semua variabel lokal saat itu) dan Watch (ekspresi yang kita pantau terus-menerus), lalu pakai Step over untuk maju satu baris atau Step into untuk masuk ke function yang dipanggil.

Kata kunci debugger; juga bisa ditulis langsung di kode sebagai titik berhenti tambahan — di Node yang dijalankan dengan --inspect-brk, eksekusi berhenti di baris itu seperti pada breakpoint.

Untuk kasus yang lebih spesifik, DevTools menawarkan dua fasilitas lanjutan. Breakpoint bersyarat hanya menghentikan program bila ekspresi tertentu bernilai benar — misalnya jumlah < 1 — sehingga program tetap cepat pada ratusan data yang sah. Panel Watch menerima ekspresi yang dipantau terus-menerus di setiap jeda, bukan sekadar membaca variabel tunggal.

Perbedaan praktis antara dua pendekatan utama chapter ini dapat dirangkum begini: transkrip dan stack trace menjawab “di baris mana program gagal dan lewat jalur mana”, sedangkan debugger menjawab “nilai apa yang ada di memori tepat sebelum baris itu dieksekusi”. Keduanya saling melengkapi — mulai dari stack trace untuk menemukan lokasi, lanjutkan dengan breakpoint untuk menyelidiki nilai.

Dengan tiga alat ini — transkrip, console.trace, dan debugger — kebanyakan kegagalan dapat dilacak sampai ke baris penyebabnya. Sampai di sini seluruh perangkat penanganan galat sinkron sudah lengkap; section berikutnya membuka topik baru, yaitu cara memecah program menjadi modul-modul.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

jejak-console.js

Unduh berkas
javascript
// jejak-console.js
// console.trace: mencetak jejak pemanggil tanpa perlu galat.

function ongkir(berat) {
  console.trace("posisi: ongkir");
  return berat * 5000;
}

function hitungTagihan() {
  return 25000 + ongkir(2);
}

console.log(hitungTagihan());

lacak-galat.js

Unduh berkas
javascript
// lacak-galat.js
// Transkrip galat untuk latihan membaca stack trace.

function hitungTotal(keranjang) {
  let total = 0;
  for (const item of keranjang) {
    total += item.harga * item.jumlah;
  }
  return total;
}

function muatKeranjang() {
  return [
    { nama: "Kabel USB", harga: 25000, jumlah: 2 },
    { nama: "Buku Catatan", harga: 15000, jumlah: 3 },
  ];
}

function jalankanKasir() {
  const keranjang = muatKeranjang();
  keranjang[1] = undefined; // penyebab: satu entri tidak terbaca
  return "Total: Rp" + hitungTotal(keranjang);
}

console.log(jalankanKasir());