When managing errors in your code, help out your future self (and anyone else who will be supporting your code in the future) by doing more than the simplest possible thing.

A common approach in Go code is to detect an error and simply pass it up the chain:

err := manager.Process()
if err != nil {
    return nil, err
}

We relearned while working on Azure Service Operator (ASO) that this approach has all the same flaws as naïve exception handling in C# – by the time details of the error appear in a log file (or are otherwise made available for consumption), it has no context and is difficult to troubleshoot.

In C#, the idiomatic way to manage this is to catch the original exception and wrap it in another for context:

try
{
    manager.Process();
}
catch (Exception ex) 
{
    throw new InvalidOperationException(
        "Unable to process events", 
        ex)
}

In ASO we made it a standard to (almost) always wrap errors with additional context before passing them up the chain, using the package github.com/pkg/errors:

err := manager.Process()
if err != nil {
    return nil, errors.Wrapf(err, "unable to process events")
}

A bonus feature of errors.Wrapf() is that it returns nil if the error passed in is already nil.

So instead of writing this at the end of a function:

err := // … elided …
if err != nil {
    return errors.Wrapf(err, "unable to process events")
}
 
return nil

You can just write:

err := // … elided …
return errors.Wrapf(err, "unable to process events")

On first encounter, this violated the principle of least surprise, but in the Go ecosystem, this is idiomatic behaviour.

The only exceptions where we didn’t wrap errors was where we could easily verify that the error already contained all the context we’d need.

We found that this made our errors much longer - and much, much, more informative, making troubleshooting much easier.

Comments

blog comments powered by Disqus
Next Post
Improve your troubleshooting by aggregating errors  11 Jun 2022
Prior Post
Keep your promises  14 May 2022
Related Posts
Browsers and WSL  31 Mar 2024
Factory methods and functions  05 Mar 2023
Using Constructors  27 Feb 2023
An Inconvenient API  18 Feb 2023
Method Archetypes  11 Sep 2022
A bash puzzle, solved  02 Jul 2022
A bash puzzle  25 Jun 2022
Improve your troubleshooting by aggregating errors  11 Jun 2022
Keep your promises  14 May 2022
When are you done?  18 Apr 2022
Archives
May 2022
2022