Go Error Handling di 2026: Pola, Wrapping, dan Pertanyaan Wawancara Teknis
Panduan lengkap tentang error handling di Go: sentinel error, custom error type, errors.Is, errors.As, error wrapping dengan fmt.Errorf %w, serta pertanyaan wawancara teknis yang sering muncul.

Penanganan error di Go mengambil pendekatan yang berbeda secara fundamental dari mayoritas bahasa pemrograman modern. Alih-alih menggunakan mekanisme exception dan blok try/catch, Go memperlakukan error sebagai nilai yang dikembalikan secara eksplisit dari setiap fungsi. Keputusan desain ini memaksa setiap jalur kegagalan terlihat jelas di kode sumber, menjadikan alur error dapat dibaca dan diuji secara langsung.
Go tidak memiliki mekanisme exception. Setiap fungsi yang mungkin gagal mengembalikan error sebagai return value terakhir. Pola ini menjadikan biaya dari mengabaikan error terlihat langsung pada level kode sumber — dan membuat alur error dapat diuji secara trivial.
Interface Error dan Mengapa Hal Ini Penting
Seluruh sistem error di Go berdiri di atas satu interface tunggal yang didefinisikan dalam standard library:
type error interface {
Error() string
}Tipe apa pun yang mengimplementasikan method Error() string memenuhi interface ini. Kesederhanaan ini mendorong komposabilitas: error dapat berupa struct, wrapper, atau bentuk apa pun selama menghasilkan representasi string.
Berikut contoh custom error type yang mendemonstrasikan pola dasar:
type NotFoundError struct {
Resource string
ID string
}
func (e *NotFoundError) Error() string {
return fmt.Sprintf("%s with ID %s not found", e.Resource, e.ID)
}NotFoundError membawa data terstruktur. Pemanggil dapat mengekstrak nama resource atau ID secara programatik tanpa perlu melakukan parsing string — keuntungan kritis ketika membangun HTTP API atau CLI tool yang memetakan error ke response spesifik.
Sentinel Error untuk Kondisi yang Sudah Diketahui
Sentinel error adalah variabel tingkat package yang merepresentasikan kondisi kegagalan spesifik yang sudah umum diketahui. Standard library menggunakan pola ini secara luas: io.EOF, sql.ErrNoRows, os.ErrNotExist.
var (
ErrNotFound = errors.New("record not found")
ErrUnauthorized = errors.New("unauthorized access")
ErrConflict = errors.New("resource conflict")
)Sentinel error paling tepat digunakan ketika pemanggil hanya perlu mengetahui apa yang gagal, bukan mengapa secara detail. Sentinel error menandakan suatu kondisi tanpa membawa konteks tambahan. Konvensi Go adalah memberi awalan Err dan menjaga pesan huruf kecil tanpa tanda baca, mengikuti Go Code Review Comments.
Mengekspor sentinel error menciptakan kontrak API publik. Package downstream akan mencocokkannya dengan errors.Is, sehingga mengganti nama atau menghapus sentinel error merupakan breaking change. Gunakan secara hemat — hanya untuk kondisi yang benar-benar perlu dijadikan titik percabangan oleh pemanggil.
Error Wrapping dengan fmt.Errorf dan Verb %w
Error wrapping, yang diperkenalkan di Go 1.13, menambahkan konteks ke sebuah error sambil mempertahankan error asli dalam sebuah rantai. Verb %w di fmt.Errorf membuat rantai ini:
func (r *UserRepo) FindByID(ctx context.Context, id string) (*User, error) {
user, err := r.db.QueryContext(ctx, "SELECT * FROM users WHERE id = $1", id)
if err != nil {
return nil, fmt.Errorf("UserRepo.FindByID(%s): %w", id, err)
}
return user, nil
}Error yang di-wrap mempertahankan seluruh rantai. Pemanggil yang berada tiga lapisan di atas masih dapat mencocokkan sentinel asli atau mengekstrak tipe aslinya. Setiap lapisan menambahkan konteks tentang di mana error terjadi tanpa mengaburkan apa yang terjadi.
Perbedaan kritis yang perlu dipahami: %w melakukan wrap (mempertahankan rantai), sementara %v memformat error sebagai string dan memutus rantai. Gunakan %v secara sengaja ketika error asli tidak boleh bocor melewati batas abstraksi — misalnya, ketika repository membungkus error driver database yang tidak seharusnya diperiksa oleh kode di lapisan service.
Inspeksi Error dengan errors.Is dan errors.As
Package errors menyediakan dua fungsi untuk menginspeksi rantai error yang sudah di-wrap, menggantikan perbandingan langsung dan type assertion.
errors.Is menelusuri rantai untuk mencari nilai error tertentu:
func handleGetUser(w http.ResponseWriter, r *http.Request) {
user, err := userService.GetByID(r.Context(), chi.URLParam(r, "id"))
if errors.Is(err, ErrNotFound) {
http.Error(w, "User not found", http.StatusNotFound)
return
}
if err != nil {
http.Error(w, "Internal error", http.StatusInternalServerError)
return
}
json.NewEncoder(w).Encode(user)
}errors.As mengekstrak tipe error tertentu dari rantai:
func errorMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if r := recover(); r != nil {
http.Error(w, "Internal error", http.StatusInternalServerError)
}
}()
next.ServeHTTP(w, r)
})
}
func mapErrorToHTTP(err error) int {
var notFound *NotFoundError
if errors.As(err, ¬Found) {
return http.StatusNotFound
}
var validationErr *ValidationError
if errors.As(err, &validationErr) {
return http.StatusBadRequest
}
return http.StatusInternalServerError
}Keunggulan utama dibandingkan type assertion: kedua fungsi ini menelusuri seluruh rantai yang di-wrap. Sebuah NotFoundError yang di-wrap tiga kali dengan fmt.Errorf dan %w tetap dapat dicocokkan.
Penanganan Error Terstruktur pada Aplikasi Berlapis
Aplikasi Go untuk produksi biasanya mengorganisasi error dalam tiga lapisan: domain error mendefinisikan kondisi bisnis, service error menambahkan konteks operasional, dan handler atau transport error memetakannya ke response eksternal.
type DomainError struct {
Code string
Message string
Err error
}
func (e *DomainError) Error() string {
if e.Err != nil {
return fmt.Sprintf("%s: %s: %v", e.Code, e.Message, e.Err)
}
return fmt.Sprintf("%s: %s", e.Code, e.Message)
}
func (e *DomainError) Unwrap() error {
return e.Err
}Method Unwrap adalah kunci yang membuat errors.Is dan errors.As dapat bekerja menelusuri rantai. Setiap custom error type yang membungkus error lain harus mengimplementasikan method ini.
Lapisan service membungkus domain error dengan konteks operasional:
func (s *UserService) Deactivate(ctx context.Context, userID string) error {
user, err := s.repo.FindByID(ctx, userID)
if err != nil {
return fmt.Errorf("deactivating user %s: %w", userID, err)
}
if user.Status == StatusInactive {
return &DomainError{
Code: "ALREADY_INACTIVE",
Message: fmt.Sprintf("user %s is already inactive", userID),
}
}
return s.repo.UpdateStatus(ctx, userID, StatusInactive)
}Siap menguasai wawancara Go Anda?
Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.
Pertanyaan Wawancara Seputar Error Handling di Go
Wawancara teknis untuk posisi Go secara rutin menguji pemahaman tentang error handling. Pertanyaan-pertanyaan berikut sering muncul dalam sesi screening dan dibahas secara mendalam di pertanyaan wawancara error handling Go.
Mengapa Go menggunakan error value alih-alih exception?
Exception menciptakan alur kontrol yang tidak terlihat. Sebuah fungsi yang melempar exception mentransfer kontrol ke catch site yang tidak diketahui, berpotensi melewati banyak stack frame. Error value di Go membuat setiap jalur kegagalan eksplisit dalam signature fungsi. Pemanggil memutuskan secara langsung bagaimana menangani error — retry, wrap, log, atau propagate. Pendekatan ini menghilangkan masalah "surprise throw" dan membuat alur error dapat dibaca tanpa bantuan tooling khusus.
Apa perbedaan antara %w dan %v di fmt.Errorf?
%w melakukan wrap terhadap error, mempertahankan rantai untuk errors.Is dan errors.As. %v memformat error sebagai string, menghasilkan error baru tanpa tautan rantai kembali ke error asli. Gunakan %w ketika pemanggil perlu dapat menginspeksi penyebab; gunakan %v ketika penyebab merupakan detail implementasi yang tidak boleh bocor melewati batas abstraksi.
Kapan sebuah fungsi sebaiknya mengembalikan error vs panic?
Panic diperuntukkan untuk situasi yang benar-benar tidak dapat dipulihkan: kesalahan pemrograman seperti index out of bounds, nil pointer dereference, atau invariant yang dilanggar yang mengindikasikan bug. Kegagalan yang dapat dipulihkan — timeout jaringan, record tidak ditemukan, input tidak valid — mengembalikan error. Aturan praktis: jika kondisi tersebut dapat terjadi selama operasi normal, kembalikan error. Jika kondisi tersebut berarti program memiliki bug, gunakan panic.
Bagaimana method Unwrap memengaruhi inspeksi rantai error?
Ketika custom error type mengimplementasikan Unwrap() error, fungsi errors.Is dan errors.As mengikuti method tersebut untuk menelusuri rantai. Tanpa Unwrap, rantai berhenti pada error tersebut. Sejak Go 1.20, error juga dapat mengimplementasikan Unwrap() []error untuk mengembalikan beberapa wrapped error, memungkinkan rantai error berbentuk tree untuk kasus seperti agregasi validation error.
Pola Error Handling yang Harus Dihindari
Beberapa anti-pattern sering muncul dalam codebase Go dan dalam sesi code review:
Mengabaikan error secara diam-diam. Blank identifier membuat hal ini mudah secara sintaksis tetapi berbahaya secara semantik:
// anti-pattern: silent error swallowing
result, _ := riskyOperation()Setiap error yang diabaikan harus merupakan pilihan yang disengaja dan terdokumentasi — bukan jalan pintas.
Logging sekaligus mengembalikan error. Pola ini menghasilkan entri log duplikat dan membingungkan pemanggil yang juga menangani error:
// anti-pattern: double handling
if err != nil {
log.Printf("operation failed: %v", err)
return err
}Solusinya: tangani error (log, kembalikan nilai default, retry) atau propagasikan. Jangan keduanya.
Pencocokan string untuk klasifikasi error. Memeriksa output err.Error() dengan strings.Contains bersifat rapuh. Pesan error bukan kontrak API — pesan dapat berubah antar versi library. Gunakan errors.Is untuk sentinel value dan errors.As untuk tipe.
Membungkus error yang melewati batas goroutine tanpa sinkronisasi. Ketika beberapa goroutine menghasilkan error secara bersamaan, gunakan errgroup.Group dari package golang.org/x/sync atau mekanisme sinkronisasi serupa. Menambahkan langsung ke shared slice tanpa mutex menciptakan data race.
Error Handling dengan errgroup untuk Operasi Konkuren
Package errgroup dari golang.org/x/sync menyediakan pola yang bersih untuk mengumpulkan error dari goroutine konkuren:
func (s *OrderService) ProcessBatch(ctx context.Context, orderIDs []string) error {
g, ctx := errgroup.WithContext(ctx)
g.SetLimit(10)
for _, id := range orderIDs {
g.Go(func() error {
if err := s.processOrder(ctx, id); err != nil {
return fmt.Errorf("processing order %s: %w", id, err)
}
return nil
})
}
return g.Wait()
}errgroup.WithContext membatalkan context turunan ketika salah satu goroutine mengembalikan error, memberi sinyal kepada goroutine lainnya untuk berhenti. Pendekatan ini menghindari pekerjaan yang sia-sia dan menyediakan satu error yang sudah di-wrap kepada pemanggil.
Go 1.24 (Februari 2025) tidak mengubah package errors. API error handling telah stabil sejak Go 1.13, dengan penambahan multi-error unwrapping melalui Unwrap() []error di Go 1.20. Proposal terkini untuk perbaikan sintaks error handling masih dalam tahap diskusi di Go issue tracker.
Kesimpulan
- Perlakukan error sebagai nilai: kembalikan secara eksplisit dan tangani di setiap call site alih-alih mengandalkan mekanisme exception tersembunyi
- Gunakan sentinel error (
var ErrX = errors.New(...)) hanya untuk kondisi yang perlu dijadikan titik percabangan oleh pemanggil, dan perlakukan sebagai API publik - Bungkus error dengan
fmt.Errorfdan%wuntuk menambahkan konteks sambil mempertahankan rantai; gunakan%vuntuk secara sengaja memutus rantai pada batas abstraksi - Lebih utamakan
errors.Isdaripada==danerrors.Asdaripada type assertion — keduanya menelusuri seluruh rantai yang di-wrap - Implementasikan
Unwrap() errorpada custom error type agar inspeksi rantai berfungsi dengan benar - Tangani atau propagasikan — jangan keduanya. Melakukan log terhadap error lalu mengembalikannya menghasilkan noise duplikat
- Gunakan
errgroupuntuk pengumpulan error konkuren alih-alih sinkronisasi goroutine secara manual - Jaga pesan error tetap huruf kecil tanpa tanda baca, mengikuti konvensi Go
Mulai berlatih!
Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.
Tag
Bagikan
Artikel terkait

Design Pattern Go: Pattern Esensial dan Pertanyaan Wawancara untuk Developer Go
Kuasai design pattern Go: Functional Options, Strategy, Factory, dan Observer. Contoh kode praktis, praktik terbaik yang idiomatis, dan pertanyaan wawancara umum untuk developer Go.

Go 1.26 Wawancara Teknis: Green Tea GC, go fix, dan Optimasi Stack
Panduan persiapan wawancara teknis Go 1.26: Green Tea garbage collector dengan pengurangan overhead 10-40%, alat go fix dengan modernizers, alokasi slice di stack, deteksi kebocoran goroutine, dan keamanan pasca-kuantum.

25 pertanyaan wawancara Go teratas: panduan lengkap developer
Kuasai wawancara Go dengan 25 pertanyaan paling sering ditanyakan. Goroutine, channel, interface, dan pola konkurensi dengan contoh kode.