9 November 2026•baca 7 menit

Debugging Python: skrip keluar dengan kode 0, tapi totalnya salah

Skrip penjumlah berjalan mulus, mencetak angka, dan angkanya salah. Tanpa traceback untuk diikuti, kamu menghentikan program di baris yang menulis nilai itu lalu membaca isi dict dari pdb.

Baca dalam bahasa Inggris

Kamu menjalankan skrip. Ia keluar dengan kode 0 dan mencetak total. Totalnya salah.

Tidak ada yang melempar error dan tidak ada peringatan tercetak, jadi tidak ada nomor baris untuk memulai.

Poin Penting

  • Jawaban salah tanpa traceback tidak punya baris untuk diikuti, jadi kamu menghentikan program di tempat nilai ditulis, bukan membaca pesan error.
  • breakpoint() memulai pdb di dalam loop, dan p mencetak nilai pada saat itu: hari yang sama datang dua kali dan tulisan kedua menggantikan yang pertama.
  • totals[day] = totals.get(day, 0) + session["minutes"] menambah, bukan menimpa, dan itulah seluruh perbaikannya.
  • Breakpoint yang tertinggal di batch job menunggu stdin selamanya, jadi checker gagal kalau breakpoint() masih ada di file.

Angka salah tanpa traceback

Skrip ini menjumlahkan menit latihan per hari. Datanya tiga sesi, dan dua di antaranya jatuh pada hari yang sama.

JSON
[
  {"date": "2026-11-03", "minutes": 20},
  {"date": "2026-11-03", "minutes": 45},
  {"date": "2026-11-04", "minutes": 30}
]

Skrip membaca file, membuat dict berisi menit per hari, lalu mencetak tiap hari diikuti total.

PYTHON
# totals.py
import json
import sys
from pathlib import Path

path = Path(sys.argv[1])
sessions = json.loads(path.read_text())

totals = {}
for session in sessions:
    day = session["date"]
    totals[day] = session["minutes"]

for day in sorted(totals):
    print(f"{day}  {totals[day]}")
print(f"total {sum(totals.values())}")

Ini hasil jalan yang sebenarnya, diukur di Python 3.14.9 pada Linux.

TEXT
$ python3 totals.py sessions.json
2026-11-03  45
2026-11-04  30
total 75

Kode keluarnya 0. Sekarang periksa hitungannya dengan tangan. Pada 2026-11-03 kamu berlatih 20 dan 45 menit, jadi hari itu seharusnya 65. Tanggal 2026-11-04 adalah 30. Totalnya seharusnya 95, bukan 75.

Tidak ada yang melempar error karena loop menimpa di tempat yang seharusnya menambah. Baris totals[day] = session["minutes"] adalah Python yang sah. Baris itu menyimpan angka di bawah sebuah key, dan kalau key-nya sudah ada, nilai lama diganti. Tidak ada TypeError untuk dilempar dan tidak ada traceback untuk dicetak.

Hentikan di titik salahnya

Sebuah traceback menunjuk baris yang gagal. Di sini tidak ada baris yang gagal, jadi kamu menghentikan program di baris yang menulis nilai salah.

Taruh breakpoint() di dalam loop, pada baris sebelum assignment, dengan indentasi yang sama seperti baris di sekitarnya. Lalu jalankan skrip seperti biasa. Pemanggilan itu memulai pdb, dan program menunggu di prompt (Pdb).

Python 3.14 mencetak direktori kerja di depan nama file. Di sini dipotong menjadi totals.py supaya barisnya muat.

TEXT
$ python3 totals.py sessions.json
> totals.py(12)<module>()
-> breakpoint()
(Pdb) p totals
{}
(Pdb) p session
{'date': '2026-11-03', 'minutes': 20}
(Pdb) n
> totals.py(13)<module>()
-> totals[day] = session["minutes"]
(Pdb) n
> totals.py(10)<module>()
-> for session in sessions:
(Pdb) p totals
{'2026-11-03': 20}
(Pdb) c
> totals.py(12)<module>()
-> breakpoint()
(Pdb) p session
{'date': '2026-11-03', 'minutes': 45}
(Pdb) p totals
{'2026-11-03': 20}
(Pdb) n
> totals.py(13)<module>()
-> totals[day] = session["minutes"]
(Pdb) n
> totals.py(10)<module>()
-> for session in sessions:
(Pdb) p totals
{'2026-11-03': 45}
(Pdb) q
Quitting pdb will kill the process. Quit anyway? [y/n] y

Kamu tidak perlu mengetik sesi persis seperti ini. Enam perintah yang penting, dan masing-masing menjawab satu pertanyaan:

  • p totals mencetak repr dari totals. Ia menjawab apa isi dict saat ini.
  • p session mencetak item yang sedang dikerjakan loop.
  • n menjalankan baris saat ini dan berhenti di baris berikutnya.
  • c melanjutkan sampai breakpoint() berikutnya, yang di sini berarti putaran loop berikutnya.
  • l menampilkan baris kode sumber di sekitar tempat kamu berhenti.
  • q keluar. Ia bertanya Quitting pdb will kill the process. Quit anyway? [y/n], karena keluar berarti mengakhiri program.

Dua kali berhenti itu membuktikan penyebabnya. Pada berhenti pertama, session adalah entri 20 menit dan totals kosong. Pada berhenti kedua, session kembali ke 2026-11-03, kali ini dengan 45, dan totals sudah berisi 20 untuk hari itu. Setelah n menjalankan assignment, totals berisi 45. Hari yang sama datang dua kali, dan tulisan kedua mengganti 20 yang pertama dengan 45.

Satu baris yang menambah, bukan menimpa

Perbaikannya satu baris:

totals[day] = totals.get(day, 0) + session["minutes"]

totals.get(day, 0) memberi menit yang sudah tersimpan untuk hari itu, atau 0 kalau harinya baru. Hapus baris breakpoint() dan filenya jadi seperti ini.

PYTHON
# totals.py
import json
import sys
from pathlib import Path

path = Path(sys.argv[1])
sessions = json.loads(path.read_text())

totals = {}
for session in sessions:
    day = session["date"]
    totals[day] = totals.get(day, 0) + session["minutes"]

for day in sorted(totals):
    print(f"{day}  {totals[day]}")
print(f"total {sum(totals.values())}")

Kedua sesi pada 2026-11-03 sekarang terhitung, dan skrip keluar dengan kode 0.

TEXT
$ python3 totals.py sessions.json
2026-11-03  65
2026-11-04  30
total 95

Checker

Hitungan tangan tadi bisa menjadi skrip yang kamu jalankan ulang. Checker ini menjalankan totals.py pada tiga sesi yang sama dan membandingkan keluarannya dengan angkamu.

PYTHON
# check_totals.py
import json
import subprocess
import sys
import tempfile
from pathlib import Path

SESSIONS = [
    {"date": "2026-11-03", "minutes": 20},
    {"date": "2026-11-03", "minutes": 45},
    {"date": "2026-11-04", "minutes": 30},
]
EXPECTED = "2026-11-03  65\n2026-11-04  30\ntotal 95"


def report(results):
    for label, ok, detail in results:
        print(f"{'PASS' if ok else 'FAIL'}  {label}" + (f"  <- {detail}" if not ok and detail else ""))
    passed = sum(1 for _, ok, _ in results if ok)
    print(f"\n{passed} of {len(results)} checks passed")
    return passed == len(results)


def main():
    folder = Path(tempfile.mkdtemp())
    data = folder / "sessions.json"
    data.write_text(json.dumps(SESSIONS))
    run = subprocess.run(
        [sys.executable, "totals.py", str(data)],
        capture_output=True, text=True, input="", timeout=20,
    )
    results = [("the script exits 0", run.returncode == 0, f"exit {run.returncode}")]
    by_day = {}
    for line in run.stdout.strip().splitlines()[:-1]:
        day, minutes = line.split()
        by_day[day] = int(minutes)
    results.append(("2026-11-03 adds up both sessions", by_day.get("2026-11-03") == 65, str(by_day.get("2026-11-03"))))
    results.append(("2026-11-04 keeps its single session", by_day.get("2026-11-04") == 30, str(by_day.get("2026-11-04"))))
    results.append(("the report prints the totals it computed", run.stdout.strip() == EXPECTED, repr(run.stdout.strip())))
    source = Path("totals.py").read_text()
    results.append(("no breakpoint() left in the script", "breakpoint()" not in source, "remove it before shipping"))
    return 0 if report(results) else 1


if __name__ == "__main__":
    sys.exit(main())

Jalankan terhadap skrip awal dan 3 dari 5 pemeriksaan lolos. Kode keluarnya 1.

TEXT
$ python3 check_totals.py
PASS  the script exits 0
FAIL  2026-11-03 adds up both sessions  <- 45
PASS  2026-11-04 keeps its single session
FAIL  the report prints the totals it computed  <- '2026-11-03  45\n2026-11-04  30\ntotal 75'
PASS  no breakpoint() left in the script

3 of 5 checks passed

Dua baris FAIL itu adalah bug yang sama, terlihat dua kali. Jalankan terhadap skrip yang sudah diperbaiki dan semua 5 pemeriksaan lolos. Kode keluarnya 0.

TEXT
$ python3 check_totals.py
PASS  the script exits 0
PASS  2026-11-03 adds up both sessions
PASS  2026-11-04 keeps its single session
PASS  the report prints the totals it computed
PASS  no breakpoint() left in the script

5 of 5 checks passed

Pemeriksaan terakhir gagal kalau breakpoint() masih ada di totals.py. Breakpoint yang tertinggal di batch job menunggu stdin selamanya, jadi kamu ingin menemukannya sebelum dirilis.

Yang tidak bisa dijawab pdb

p menunjukkan nilai pada saat itu, bukan riwayatnya. Untuk tahu bagaimana sebuah nilai sampai di sana, kamu melangkah dengan n dan mencetak lagi, seperti di transkrip tadi.

Breakpoint juga menghentikan batch job. Program tidak selesai sampai seseorang menjawab di prompt.

PYTHONBREAKPOINT=0 mematikan breakpoint tanpa mengedit file. Pada skrip ini, ia hanya menyembunyikan debugger.

TEXT
$ PYTHONBREAKPOINT=0 python3 totals.py sessions.json
2026-11-03  45
2026-11-04  30
total 75

Breakpoint dilewati, skrip keluar dengan kode 0, dan bug-nya tetap ada.

Kalau angka salah dan tidak ada yang melempar error, hentikan program di baris yang menulis nilainya, karena traceback tidak akan datang.

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
Struktur DataTentang Struktur Data LinkedList

Linked list adalah rantai linear berisi node, dan setiap node menyimpan data plus pointer ke node berikutnya. Ini bentuk rantainya, ongkosnya dibanding array, dan implementasi paling sederhana dalam Java, Python, serta JavaScript.

Untuk developer yang belajar dari kesulitan

© 2026 Inva.dev.