Hands On Golang Error Handling

Ahmed Ghazey
4 min readJul 5, 2022

Every program encounters errors and we should plan for them.

Sometimes handling an error can be as simple as logging and exit the program, but some scenarios are different and require additional action, you may need to close files or network connection, or otherwise clean up, so you program doesn’t leave mess behind.

let’s see simple program reading numbers from file

OpenFile function take fileName as parameter and returns file handle and error in case of error like access error, file not found err, etc…

CloseFile function take file handle as input close the file and return void

GetFloats function take fileName as parameter and returns float64 slice and error in case of error like number format, errors from OpenFile function and scanner.Err(), etc…

now let’s check the main function it will contain simple logic calling GetFloats and calculate the sum of the slice.

if the file numbers.txt looks like this file

20.1
10.3
4.6

the output will looks like :

Opening numbers.txt
Closing file
Sum: 35.0

if we give this program an improperly formatted file, we will run into problems. A file with line that can’t be parsed to float64 value

20.1
notValidNumber
4.6

Now we expect an error let’s check the output of the program we will find that not all the program statements are executed after the error, any errors will prevent the file from being closed.

Opening numbers.txt
2022/05/22 01:17:03 strconv.ParseFloat: parsing "notValidNumber" : invalid syntax
exit status 1

Currently we have two problems we want to solve:
1- we need to close the open resources
2- we need to resume our program normally and exit gracefully.

Now, closing the file may not seem as a big deal, and for small programs that only use one file it might be okay, but each file that’s left open continues to consume operating system resource, which will lead eventually to poor performance or may cause program to fail. The best practice is to ensure to close open resources even in fail.

How can we accomplish this goal? the GetFloats function is setup to immediately exit if it encounters any error, well the golang authors made something like finally clause in languages like java or C# and it is not also like finally as behavior, any function call preceded by defer will be executed before the return from the current function.

Deferring functions calls

defer keyword ensure a function call takes place even if the calling function exit early let’s update our GetFloats function to use defer

using defer we now ensure that close file will be called we GetFloats exit whether it completes normally or or there is an error.

in case of wrong file

Opening numbers.txt
Closing file
2022/05/22 02:17:03 strconv.ParseFloat: parsing "notValidNumber" : invalid syntax
exit status 1

Golang has couple more features to help in error handling which are panic and recover

Panic and Recover

Panic is caused by runtime error or you call panic function explicitly, panic function expects single argument which satisfy the empty interface, this argument is converted to string if necessary and printed as part of the panic log.

Stack traces

Each function called should return to the caller function and this is common between most of the programming language, golang keeps a call stack, when function panic a stack trace is included in the panic output log, this might help in determining the root cause of panic.

panic: too deep

goroutine 1 [running]:
main.Three(...)
/tmp/sandbox3543881512/prog.go:16
main.Two(...)
/tmp/sandbox3543881512/prog.go:13
main.One(...)
/tmp/sandbox3543881512/prog.go:10
main.main()
/tmp/sandbox3543881512/prog.go:6 +0x29

Program exited.

Deferred function

deferred function will be executed even in panic before function return to its caller.

deferred in two
panic: too deep

goroutine 1 [running]:
main.Three(...)
/tmp/sandbox1894646871/prog.go:19
main.Two()
/tmp/sandbox1894646871/prog.go:16 +0x74
main.One(...)
/tmp/sandbox1894646871/prog.go:12
main.main()
/tmp/sandbox1894646871/prog.go:8 +0x18

Program exited.

Calling panic is rarely the ideal way to handle error, things like bad user input, network failure, file accessibility..etc; should be handled through normal error and handled gracefully.
Panic should be used only when it is impossible to continue program execution like a bug in the program.

Recover

Panicking causing program to crash with its stack trace we might want to show user error message and continue, go provide developers with recover function which is able to stop panicking state and allow the program to exit gracefully.

Important notes about recover:
1- call recover when program is panicking this will stop the panic
2- call recover inside a panic function is useless because the panic will continue anyway

Exiting Normally

Program exited.

Recover return type

Recover function return whatever you pass as an argument to panic function during normal execution it will return nil.

recovering from: "disaster"
main received: ""

Program exited.

you can see s value is string zero value because of panic occurred before foo return a value.

to return a value from recover you should use a named return value it is a golang feature, you can use it to return error from panicked functions.

recovering from: "disaster"
main received: "before during"

Program exited.

The End

I hope this article give you insight on how to work with golang errors handling, I tried to make it integrated as much as I can.

Resources

1- Head First Go Book for Jay McGavern
2- this tutorial
3- few youtube videos

--

--

Ahmed Ghazey

Seasoned Senior Staff Software Engineer with a proven track record of delivering complex software solutions, managing teams, and providing technical leadership.