11 Oktober 2026•baca 4 menit

Jawabannya: Kumpulkan Semua Masalah Dulu, Baru Lapor

Pesan error yang berguna menjawab di mana, apa, dan harusnya bagaimana — sekaligus, tanpa berhenti di masalah pertama.

Repo lab

Lab-nya ada di folder 0005.error-messages

Kode yang sengaja dibuat salah, dataset-nya, dan target make check-nya ada di repo publik. Semuanya jalan di dalam Docker.

Ini artikel jawabannya. Kalau kamu belum mengerjakan lab 0005 dan masih mau mencoba sendiri, mendingan tutup dulu halaman ini. Sisanya di bawah adalah jawabannya, lengkap dengan angka pengukurannya.

Poin Penting

  • Satu baris jelek nggak boleh menghentikan seluruh proses. Laporkan, lalu lanjut ke baris berikutnya.
  • Nomor baris di file dimulai dari enumerate(pembaca, start=2) — baris 1 adalah judulnya, bukan data.
  • IndexError itu artinya belum memvalidasi. Periksa jumlah kolom dulu sebelum menyentuh isi baris.
  • Kode keluar itu kontrak: 0 beres, 1 ada baris bermasalah, 2 cara pakainya salah.

Angka lengkapnya

Dua keadaan, diukur dengan scripts/check.py yang sama, di mesin yang sama, tanpa mengubah data/ atau scripts/check.py sedikit pun:

varian hijau catatan
kondisi awal (branch main) 1 dari 7 cuma A hijau: periksa() melempar int(baris[2]), dan siapa pun yang memanggilnya berhenti di situ
semua masalah dikumpulkan, nomor baris start=2, kode keluar 0/1/2 7 dari 7 —

Angka konkretnya, kalau kamu mau mencocokkan dengan yang kamu lihat di laptopmu. Di kondisi awal, beberapa.csv mencuatkan ValueError: invalid literal for int() with base 10: 'dua puluh' dan cuma 0 dari 4 masalah yang dilaporkan. kosong.csv malah tanpa keluhan sama sekali, lalu tetap bilang 3 baris diimpor dengan kode keluar 0, padahal ada baris tanpa nama di dalamnya. kolom.csv meledak dengan IndexError: list index out of range, 0 dari 2 masalah. Dan tidak-ada.csv memberi FileNotFoundError dengan kode keluar 1, bukan 2.

Di branch solusi, make demo menghasilkan satu baris laporan untuk tiap masalah — empat baris untuk beberapa.csv, dua untuk kolom.csv — dengan kode keluar 0/1/2 sesuai keadaan.

Kenapa kondisinya begini

periksa() mencetak, bukan mengembalikan — dan melempar di tengah jalan

PYTHON
umur = int(baris[2])   # ValueError kalau isinya bukan angka
if umur < 1 or umur > 120:
    print("umur tidak masuk akal, dilewati")

Ada dua cacat di dua baris itu. Pertama, int() melempar, dan main() nggak menangkapnya: satu baris jelek menghentikan seluruh proses, jadi masalah sesudahnya nggak pernah keburu dilaporkan. Kedua, satu-satunya diagnostik cuma print yang nggak menyebut baris maupun kolomnya.

Yang bikin jebakan ini licin: komentar di atasnya justru membenarkan dua-duanya — "Python sudah memberi pesan yang jelas", "yang bikin sistem rusak cuma umur". Kodenya kelihatan hati-hati. Padahal aturan yang kamu butuhkan kebalikannya: satu baris jelek nggak boleh menghentikan seluruh proses. Baris itu dilaporkan, lalu perulangan jalan terus ke baris berikutnya.

Nomor baris itu urusan yang memanggil, bukan yang memvalidasi

periksa() sebaiknya mengembalikan daftar masalah dan nggak tahu apa-apa soal nomor baris. Yang menempelkan nomornya adalah main(), lewat enumerate(pembaca, start=2). Angka 2 itu bukan detail sepele: baris 1 di file adalah judulnya, jadi baris data pertama ada di baris 2.

Kalau kamu mulai dari start=1, angka yang kamu laporkan salah persis satu dari yang dilihat pengguna di editornya. Inilah off-by-one yang paling gampang lolos dari mata sendiri, karena layarnya "kelihatannya benar". Yang memisahkan keduanya cuma satu hal: nomor baris di file itu urusan yang memanggil, sedangkan index data itu urusan yang memvalidasi.

IndexError sama saja belum memvalidasi

kolom.csv punya baris dengan 2 kolom dan ada juga yang 4 kolom, padahal judulnya cuma punya 3. Menyentuh baris[2] sebelum memastikan jumlah kolomnya cukup cuma memindahkan lokasi crash-nya. IndexError di sini artinya belum memvalidasi — bukan tanda data yang rumit.

Urutan yang benar: periksa jumlah kolom lebih dulu. Kalau len(baris) nggak sama dengan jumlah kolom yang diharapkan, laporkan sebagai satu masalah di kolom jumlah kolom, lalu jangan lanjut memeriksa isi baris itu. Kolom per kolom belum bisa dipercaya sebelum jumlahnya cocok.

Kode keluar adalah bagian dari kontrak

Script seperti ini dipanggil dari make, cron, atau pipa shell. Pemanggilnya nggak membaca layar; dia membaca exit code. "File tidak ada" dan "file ada tapi isinya ada yang salah" itu dua hal yang beda bagi pemanggil — yang pertama artinya salah pakai, yang kedua artinya datanya perlu diperbaiki.

Karena itu kode keluarnya dibedakan: 0 untuk beres, 1 untuk ada baris bermasalah, 2 untuk cara pakainya yang salah (file nggak ada, argumen kurang). Kondisi awal cuma punya satu kode untuk hampir semua kegagalan, jadi pemanggil otomatis nggak bisa memutuskan harus mengulang, berhenti, atau melanjutkan.

Bentuk solusinya

Validasi jadi fungsi yang mengumpulkan, bukan yang melempar:

PYTHON
def periksa(baris):
    masalah = []
    if len(baris) != len(KOLOM):
        masalah.append(("jumlah kolom", "... seharusnya ..."))
        return masalah
    nama, email, umur = (sel.strip() for sel in baris)
    if not nama:
        masalah.append(("nama", "kolom nama kosong, padahal harus diisi"))
    if "@" not in email:
        masalah.append(("email", f"'{email}' bukan alamat email karena tidak ada tanda '@'"))
    try:
        umur_angka = int(umur)
    except ValueError:
        masalah.append(("umur", f"'{umur}' bukan angka — isi dengan bilangan bulat, misalnya 25"))
    return masalah

lalu di main() nomor barisnya ditempel dari baris file:

PYTHON
for nomor, baris in enumerate(pembaca, start=2):   # baris 1 file = judul
    for kolom, pesan in periksa(baris):
        sys.stderr.write(f"baris {nomor}: {kolom} — {pesan}\n")

Satu baris yang benar mengembalikan daftar kosong, jadi nggak ada yang dilaporkan. Satu baris yang salah bisa mengembalikan lebih dari satu masalah sekaligus — di beberapa.csv ada satu baris yang nama kosong dan emailnya tanpa @. Perhatikan juga bahwa try/except-nya kecil dan spesifik, cuma membungkus int(umur). Jangan bungkus semuanya dengan satu try/except besar di main(): itu memang menghilangkan stack trace, tapi sekalian menghentikan program di baris jelek pertama, dan pekerjaanmu balik ke titik awal.

Yang perlu kamu bawa dari sini

Pesan error yang berguna menjawab tiga hal — di mana, apa yang salah, dan harusnya bagaimana — dan jawabannya untuk semuanya sekaligus, bukan cuma yang pertama kebetulan ditemui. Program yang berhenti di masalah pertama cuma menjawab sepertiganya. Sisa waktu yang kamu hemat di situ, kamu bayar mahal di telepon yang berdering.

Satu kebiasaan saja yang berubah: sebelum menulis raise, tanya dulu siapa yang akan membaca pesannya.

Biar makin jago ngoding.

Yuk gabung bareng temen-temen developer lainnya buat dapet update teknologi, tips, dan tutorial santai tiap minggu.

Biar Nggak Kudet

Update teknologi, tips ngoding, dan insight santai langsung ke email kamu tiap minggu.

Santai, anti spam. Bisa berhenti langganan kapan aja.

Baca Selanjutnya

Lihat semua

© 2026 Inva.dev.