Error Handling in Go: try/catch and Beyond — A Complete Guide

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction to Error Handling in Go



Go (Golang) is a modern compiled language developed at Google. One of its key features is its approach to error handling, which is fundamentally different from the familiar try/catch mechanism in Java, Python, or JavaScript. Go does not have exceptions in the classical sense. Instead, the language uses explicit, "manual" error passing through return values. This approach may seem inconvenient to beginners, but it makes code more predictable, readable, and resistant to hidden bugs.



In this article, we will thoroughly examine the philosophy of error handling in Go: from the basic error interface to the panic and recover mechanisms. You will learn why Go abandoned try/catch, and how to write reliable code without sudden crashes.



Why is there no try/catch in Go?



The developers of Go (Rob Pike, Ken Thompson, and Robert Griesemer) consciously decided against exceptions. The main reasons are:



  • Explicitness: An error is simply a value that a function returns explicitly. The programmer always sees that a function can complete with an error.
  • Uncluttered execution flow: In languages with exceptions, code often "jumps" into a catch block, breaking linearity. In Go, the execution flow remains predictable.
  • No hidden errors: Exceptions can go uncaught, leading to crashes. In Go, you are required to handle the error (or explicitly ignore it using _).


Instead of try/catch, Go offers a simple but powerful idiom:



result, err := doSomething()if err != nil {    // Error handling    log.Fatal(err)}// Work with result


This pattern is ubiquitous in Go. It does not require special constructs and makes error handling a part of the normal code flow.



The error interface and creating custom errors



Built-in error interface



In Go, errors implement the built-in interface:



type error interface {    Error() string}


Any type that has an Error() string method automatically satisfies this interface. The standard errors.New() function creates a simple error:



import "errors"

err := errors.New("something went wrong")fmt.Println(err.Error()) // "something went wrong"


Creating custom error types



For detailed information, custom structures are created:



type MyError struct {    Code    int    Message string}

func (e *MyError) Error() string { return fmt.Sprintf("error %d: %s", e.Code, e.Message)}

func riskyOperation() error { return &MyError{Code: 404, Message: "not found"}}

func main() { err := riskyOperation() if err != nil { // type assertion to get details if myErr, ok := err.(*MyError); ok { fmt.Printf("Code: %d, Message: %s", myErr.Code, myErr.Message) } }}


This allows you to pass any additional data along with the error: HTTP statuses, error codes, timestamps, etc.



Working with panic and recover



Although Go avoids exceptions, it has a panic mechanism for critical situations (e.g., array index out of bounds or division by zero). panic is not a replacement for try/catch, but an emergency switch.



When to use panic?

  • For unexpected states that cannot be handled (e.g., an error during program initialization).
  • In tests and prototypes (but not in production code without extreme necessity).


The recover mechanism



recover() allows you to "catch" a panic and continue execution. It only works inside a deferred function (defer):





                
                

Blogs

Book Recommendations