Go Error Handling Lanjut
Definisi
Di Go, error adalah nilai biasa yang di-return dari fungsi, bukan exception. Konvensi umumnya: fungsi yang mungkin gagal mengembalikan (value, error), dan caller wajib mengecek error sebelum memakai value-nya.
value, err := doSomething()
if err != nil {
return fmt.Errorf("doSomething gagal: %w", err)
}Kapan dipakai
Pakai error untuk setiap operasi yang bisa gagal di runtime: I/O, network, parse, validasi, query database. Error bukan untuk mengontrol alur normal; kalau itu alur bisnis biasa (misal "keranjang kosong"), pakai nilai return biasa, bukan error.
Cara kerja
Sentinel error
Error yang dideklarasikan sebagai variabel package-level, dipakai untuk perbandingan identitas.
var ErrNotFound = errors.New("tidak ditemukan")
func Find(id int) (*Item, error) {
if !exists(id) {
return nil, ErrNotFound
}
// ...
}Caller membandingkan dengan errors.Is:
if errors.Is(err, ErrNotFound) {
// tangani khusus "tidak ditemukan"
}Custom error
Error dengan konteks tambahan lewat tipe sendiri.
type ValidationError struct {
Field string
Msg string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("validasi gagal di %s: %s", e.Field, e.Msg)
}Cek tipenya dengan errors.As:
var ve *ValidationError
if errors.As(err, &ve) {
log.Printf("field bermasalah: %s", ve.Field)
}Error wrapping
Bungkus error asli supaya konteks tidak hilang, pakai %w di fmt.Errorf.
if err != nil {
return fmt.Errorf("simpan user %d: %w", id, err)
}Tanpa %w (pakai %v), error asli tidak bisa di-deteksi lagi dengan errors.Is/errors.As.
Panic dan recover
panic memotong eksekusi hingga ada recover di defer. Dipakai hanya untuk kondisi yang bener-bener tidak terduga (bug, invariant rusak), bukan untuk error bisnis biasa.
func safeRun(fn func()) (recovered any) {
defer func() {
recovered = recover()
}()
fn()
return
}Struktur alur
Contoh
func ProcessOrder(id int) error {
order, err := repo.Get(id)
if err != nil {
// sentinel tetap terdeteksi oleh caller
return fmt.Errorf("ambil order: %w", err)
}
if err := validate(order); err != nil {
// custom error ikut terbawa, bisa di-As oleh caller
return fmt.Errorf("validasi order %d: %w", id, err)
}
return nil
}Catatan implementasi
- Selalu cek error sebelum memakai nilai kembaliannya; jangan asumsikan
valuevalid saaterr != nil. - Gunakan
%wsaat wrap, bukan%v, kalau ingin caller bisa inspeksi error asli. - Sentinel error sebaiknya diekspor (
ErrNotFound) supaya bisa dibandingkan lintas package. errors.Asbutuh pointer ke tipe error yang tepat; salah tipe akan gagal di-deteksi.
Kesalahan umum
- Error shadowing:
if err := do(); err != nilmembuat variabelerrbaru di scope sempit, sehingga error di luar tidak terlihat. - Kehilangan konteks: wrap dengan
%vatau string biasa, sehingga stack asli hilang. - Panic untuk error bisnis:
panicdipakai untuk "user tidak punya saldo" — ini membuat recovery sulit dan menutupi alur. - Mengabaikan error:
doSomething()tanpaerrdicek; error diam-diam hilang.