Node.js adalah runtime yang sangat populer di kalangan developer JavaScript, tapi tak urung dari berbagai error yang kerap muncul baik saat development maupun production. Error-error ini seringkali membingungkan karena pesan yang ditampilkan terkesan teknis dan sulit diuraikan, terutama bagi developer yang baru bermigrasi dari bahasa pemrograman lain. Artikel ini akan mengupas tuntas pesan-pesan error paling umum di Node.js, apa artinya, kenapa muncul, dan bagaimana cara memperbaikinya secara permanen.


Pesan Error & Artinya

Ketika Node.js mengalami kegagalan, ia akan melempar (throw) sebuah error dengan pesan tertentu ke stderr. Berikut adalah beberapa kutipan pesan error asli yang paling sering ditemui:

1. Module Not Found

Error: Cannot find module 'express'

Pesan ini muncul ketika Node.js tidak dapat menemukan modul yang kamu require() atau import. Arti singkatnya: paket atau file yang kamu referensikan tidak ada di lokasi yang dicari oleh Node. Ini adalah error paling klasik dan hampir setiap developer Node.js pernah mengalaminya setidaknya sekali.

2. Port Sudah Dipakai (EADDRINUSE)

Error: listen EADDRINUSE: address already in use :::3000

Pesan ini berarti aplikasi mencoba mendengarkan (listen) di port 3000, tapi port tersebut sudah ditempati oleh proses lain. Dalam bahasa Indonesia sederhana: "Port yang kamu pakai sudah dipakai program lain, jadi aplikasimu nggak bisa start."

3. File Tidak Ditemukan (ENOENT)

Error: ENOENT: no such file or directory, open './config.json'

ENOENT adalah singkatan dari "Error NO ENTry", yang dalam konteks filesystem berarti file atau direktori yang ingin diakses tidak ada. Node.js gagal membuka config.json karena file tersebut memang tidak berada di path yang ditentukan.

4. Unhandled Promise Rejection

UnhandledPromiseRejectionWarning: TypeError: Cannot read properties of undefined (reading 'name')

Error ini muncul ketika sebuah Promise di-reject tapi tidak ada handler .catch() atau try/catch yang menangani penolakan tersebut. Di dalamnya terdapat TypeError yang menandakan kamu mencoba mengakses properti name dari nilai undefined—sesuatu yang tidak mungkin dilakukan di JavaScript.

5. SyntaxError: Unexpected token

SyntaxError: Unexpected token 'import'

Pesan ini muncul ketika Node.js menemukan sintaks yang tidak dipahami di versi yang sedang digunakan. Contoh klasik: menggunakan import ES Module padahal Node.js menjalankan file sebagai CommonJS (require).

Kenapa error-error ini muncul? Intinya, Node.js sangat ketat soal resolusi modul, manajemen port, akses filesystem, dan penanganan asynchronous. Setiap pelanggaran aturan di salah satu area tersebut akan langsung dilempar sebagai error tanpa toleransi.


Kenapa Ini Terjadi

Berikut adalah root cause dari error-error di atas, diurutkan dari yang paling sering terjadi:

  1. Modul belum di-install atau salah install path — Ini adalah penyebab paling dominan dari Cannot find module. Kamu mungkin lupa menjalankan npm install setelah clone repository, atau paket ter-install di node_modules proyek lain karena salah direktori. Cara memastikan: jalankan ls node_modules | grep <nama-paket> dan periksa apakah folder paket tersebut ada.

  2. Proses sebelumnya masih berjalan di port yang sama — Penyebab paling umum dari EADDRINUSE. Sering terjadi ketika kamu menghentikan server dengan Ctrl+C tapi proses zombie masih nyangkut, atau kamu menjalankan dua instance aplikasi sekaligus. Cara memastikan: jalankan lsof -i :<port> di Linux/macOS atau netstat -ano | findstr :<port> di Windows untuk melihat PID proses yang memonopoli port tersebut.

  3. Path file salah atau working directory berbeda — Penyebab utama ENOENT. Relative path di Node.js di-resolve berdasarkan current working directory (CWD), bukan lokasi file script. Jika kamu menjalankan node src/app.js dari direktori yang berbeda, path ./config.json akan merujuk ke CWD, bukan folder src/. Cara memastikan: tambahkan console.log(process.cwd()) dan bandingkan dengan __dirname untuk melihat apakah keduanya sesuai harapan.

  4. Versi Node.js tidak mendukung sintaks yang digunakan — Penyebab SyntaxError: Unexpected token. Misalnya, import/export ES Module baru didukung penuh di Node.js v14+ dengan "type": "module" di package.json, atau fitur-fitur terbaru seperti top-level await hanya ada di v14.8+. Cara memastikan: jalankan node --version dan cross-check dengan dokumentasi fitur yang kamu gunakan.

  5. Asynchronous code tanpa error handling — Penyebab UnhandledPromiseRejection. Banyak developer menggunakan .then() tanpa .catch(), atau await tanpa try/catch, sehingga ketika Promise gagal, error-nya "menggantung" tanpa penanganan. Cara memastikan: cari semua pemanggilan async di codebase dan verifikasi apakah setiap panggilan memiliki mekanisme catch.


Cara Memperbaiki

Perbaikan 1: Cannot find module

Langkah pertama, pastikan kamu berada di direktori proyek yang benar:

cd /path/to/your/project
npm install

Output yang diharapkan:

added 157 packages in 12s

Jika paket tertentu masih tidak ditemukan, install secara eksplisit:

npm install express

Jika kamu menggunakan monorepo atau workspace, pastikan paket ter-install di lokasi yang tepat. Verifikasi dengan:

node -e "console.log(require.resolve('express'))"

Output yang diharapkan menunjukkan path absolut ke modul, misalnya:

/home/user/project/node_modules/express/index.js

Fix permanen: Selalu jalankan npm install (atau npm ci di CI/CD) setelah setiap perubahan package.json. Tambahkan script postinstall jika diperlukan.

Perbaikan 2: EADDRINUSE

Temukan dan matikan proses yang memonopoli port:

Linux/macOS:

lsof -ti :3000 | xargs kill -9

Windows:

for /f "tokens=5" %a in ('netstat -ano ^| findstr :3000') do taskkill /PID %a /F

Output yang diharapkan:

Killed

Atau, lebih elegan, ubah port aplikasimu ke port yang kosong:

PORT=3001 node server.js

Verifikasi dengan menjalankan ulang aplikasi dan pastikan tidak ada error EADDRINUSE lagi. Aplikasi seharusnya log: Server listening on port 3001.

Fix permanen: Gunakan tool seperti kill-port sebagai script pre-start, atau konfigurasi aplikasi untuk otomatis mencari port yang available.

Perbaikan 3: ENOENT

Jangan gunakan relative path berbasis CWD. Ganti:

// ❌ Rentan error karena bergantung pada CWD
const config = require('./config.json');

dengan:

// ✅ Menggunakan __dirname sebagai referensi stabil
const config = require(path.join(__dirname, 'config.json'));

Atau jika menggunakan ES Module:

import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const config = JSON.parse(readFileSync(join(__dirname, 'config.json'), 'utf-8'));

Verifikasi dengan menjalankan aplikasi dari berbagai direktori dan memastikan file selalu ditemukan.

Perbaikan 4: SyntaxError Unexpected token

Jika kamu ingin menggunakan ES Module, tambahkan di package.json:

{
  "type": "module"
}

Atau rename file dari .js ke .mjs. Jika sebaliknya kamu ingin tetap di CommonJS, gunakan require() dan module.exports saja.

Verifikasi:

node --input-type=module -e "import('fs').then(m => console.log('ESM OK'))"

Output: ESM OK

Perbaikan 5: UnhandledPromiseRejection

Bungkus setiap operasi async dengan error handling:

// ❌ Tanpa handler
const data = await fetch('/api/users');

// ✅ Dengan try/catch
try {
  const data = await fetch('/api/users');
} catch (error) {
  console.error('Gagal mengambil data:', error.message);
}

Sebagai safety net global, tambahkan di entry point aplikasi:

process.on('unhandledRejection', (reason, promise) => {
  console.error('Unhandled Rejection:', reason);
  // Lakukan graceful shutdown jika perlu
  process.exit(1);
});

Verifikasi: sengaja throw error di dalam Promise dan pastikan aplikasi menangkapnya dengan benar alih-alih crash tanpa pesan.


Supaya Tidak Terulang

Pencegahan selalu lebih murah daripada perbaikan. Berikut konfigurasi dan best practice yang bisa meminimalkan error-error di atas kembali muncul.

Konfigurasi package.json yang robust:

{
  "type": "commonjs",
  "engines": {
    "node": ">=18.0.0"
  },
  "scripts": {
    "prestart": "node -e \"const v=process.versions.node.split('.').map(Number); if(v[0]<18) { console.error('Butuh Node >=18'); process.exit(1); }\"",
    "start": "node server.js"
  }
}

Dengan konfigurasi engines, kamu memaksa versi Node.js minimum. Ditambah script prestart yang memvalidasi versi sebelum aplikasi berjalan, error karena sintaks yang tidak didukung bisa dicegah sejak awal.

Gunakan .nvmrc untuk konsistensi versi di tim:

18.17.0

Semua member tim tinggal menjalankan nvm use dan versi Node.js akan otomatis sesuai.

Untuk masalah port, gunakan environment variable dengan fallback:

const PORT = process.env.PORT || 0; // Port 0 = OS otomatis assign port kosong
const server = app.listen(PORT, () => {
  console.log(`Server berjalan di port ${server.address().port}`);
});

Menggunakan port 0 adalah trik yang kurang dikenal tapi sangat efektif: OS akan otomatis mencarikan port yang available, sehingga EADDRINUSE tidak akan pernah terjadi lagi saat development.

Best practice tambahan:

  • Selalu commit package-lock.json ke repository agar setiap npm ci menghasilkan dependency tree yang identik.
  • Gunakan npm ci alih-alih npm install di environment CI/CD karena npm ci melakukan clean install berdasarkan lock file dan akan error jika lock file tidak sinkron dengan package.json—ini mencegah inkonsistensi modul.
  • Pasang eslint dengan rule no-unhandled-promises untuk mendeteksi Promise yang tidak ditangani sebelum kode mas