Messages and errors with goroutines

Daniel Carvallo
2 min readJul 13, 2021
Photo by Christopher Gower on Unsplash

A few days ago I ran into a problem related to goroutines, specifically it made it a bit difficult for me to debug messages that were not displayed. The way the goroutine was called caused some error messages to get lost, making it almost impossible to tell at a glance where the error was.

After fixing the error I decided to experiment a bit with the way that the errors caused by that goroutine can be saved.

Photo by Arian Darvishi on Unsplash

As you know, one of the easiest ways to save messages and errors from a goroutine is through an external structure, which is updated when an error is found. More elements can be used in the structure, but at the moment I will only use two to

type Errors struct { 
Message string
Error error
}

Thus, every time there is an error we send the error and message through the channel used; ie:

ch <- Errors{"NOK:", err}

So each error found would be tracked for later analysis. However, in my case the goroutine was used within a loop so sometimes I received a very curious message.

fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
/Users/daniel/app.go:56 +0x526
exit status 2

Well, it turns out that the number of channels was not the expected since sometimes the goroutine was not called by the program, let’s say that no error occurred and therefore another part of the code that verified the messages of each channel failed.

for _ = range slice {
c := <-ch

if c.Error != nil {
fmt.Println(c.Error.Error())
}
}

The problem was that they had forgotten to add a no error message to the goroutine. It was not only necessary to store the errors but also the successful cases.

if strings.Contains(file, "BadWord") {  
ch <- Errors{"NOK:", fmt.Errorf("File with BadWord " + file)}
return
}
fmt.Println(file)
ch <- Errors{"OK:", nil}

The application stopped crashing once the missing part was fixed, now it only remained to filter the true errors and ignore the null errors.

for _ = range slice {  
c := <-ch
if c.Error != nil {
chErr = append(chErr, c.Error.Error())
}
}

I added a gist for reference, I am sure it will be useful for people who do not find any answer for a problem like this one. This little gist could be the beginning of a much more elaborate and robust solution.

https://gist.github.com/elulcao/fa695e98356851e6b53893e0f25f4295

--

--

Daniel Carvallo

My primary goal of doing this is the intellectual curiosity, the seduction of adventure.