How to propagate multiple errors using the new fmt.Errorf wrapping

3,250 views
Skip to first unread message

Alex

unread,
Aug 8, 2019, 4:33:45 AM8/8/19
to golang-nuts
Hey.

I am looking at the error wrapping of 1.13-beta at the moment to figure out best practices on error handling with this new capability.

My question now is:
If I want to propagate errors up some layers I don't know how to wrap additional errors on the path up.

as an example:

var (
Layer1Error    = errors.New("some error on layer 1")
Layer2Error    = errors.New("some error on layer 2")
)

func main() {
err := callLayer1Function()

// do something with error
}

func callLayer1Function() error {
err := callLayer2Function()

// how to not lose layer2 error but also append a new layer1 error ?
// this does not work, since you fully lose layer1 error
// with pkg/err
return fmt.Errorf("some specific layer 2 error message: %w", Layer2Error)
}

func callLayer2Function() error {
// wrap an error of Layer1 here
return fmt.Errorf("some specific layer1 error message: %w", Layer1Error)
}



Any idea or best practices to propagate multiple errors up the stack?

Alex

unread,
Aug 9, 2019, 9:39:24 AM8/9/19
to golang-nuts
Sorry, mixed things up in the code with layer1 and layer2... 


var (
Layer1Error    = errors.New("some error on layer 1")
Layer2Error    = errors.New("some error on layer 2")
)

func main() {
err := callLayer1Function()

// do something with error
}

func callLayer1Function() error {
err := callLayer2Function()

// how to not lose layer2 error but also append a new layer1 error ?
// this does not work, since you fully lose layer1 error
// with pkg/err
return fmt.Errorf("some specific layer 1 error message: %w", Layer1Error)
}

func callLayer2Function() error {
// wrap an error of Layer2 here
return fmt.Errorf("some specific layer2 error message: %w", Layer2Error)
}


Agniva De Sarker

unread,
Aug 9, 2019, 12:02:23 PM8/9/19
to golang-nuts
This is the right way. What is the issue you are facing ? See https://tip.golang.org/pkg/errors/ for more info.

You can check for Layer1Error and Layer2Error using the Is function

errors.Is(err, Layer1Error)

errors.Is(err, Layer2Error)

Alex

unread,
Aug 9, 2019, 1:13:11 PM8/9/19
to golang-nuts
Hi Agniva,

the problem is: In the main function is no information that there was an Layer2 error (layer2 error is not included in the error anymore).
I don't know how to take the error from layer2 and wrap another error (layer1-error) around it.

You can only use the verb "%w" once in a fmt.Errorf() function afaik.
So if you have a wrapped error object e1, how would you enrich / wrap this with another error e2?


Thanks,
Alex

Agniva De Sarker

unread,
Aug 9, 2019, 1:35:42 PM8/9/19
to golang-nuts
I see. One way is to create a wrapper error type in layer1, which takes a layer2 error.  Just like os.PathError.

package main

import (
"errors"
"fmt"
)

var (
// Layer1Error = errors.New("some error on layer 1")
Layer2Error = errors.New("some error on layer 2")
)

type Layer1Error struct {
internal error
}

func (le *Layer1Error) Error() string {
return fmt.Sprintf("layer2 error: %v", le.internal)
}

func (le *Layer1Error) Unwrap() error {
return le.internal
}

func main() {
err := callLayer1Function()
fmt.Println(errors.Is(err, Layer2Error))
var l2err *Layer1Error
fmt.Println(errors.As(err, &l2err))
}

func callLayer1Function() error {
err := callLayer2Function()
return &Layer1Error{err}
}

func callLayer2Function() error {
// wrap an error of Layer2 here
return fmt.Errorf("some specific layer2 error message: %w", Layer2Error)
}

Alex

unread,
Aug 9, 2019, 1:51:57 PM8/9/19
to golang-nuts
Yes, sure. This is always possible. :-)

But this is kind of writing your own error wrapper. I was just wondering if this is somehow possible with the new error wrapper like it was with https://github.com/pkg/errors.

Agniva De Sarker

unread,
Aug 10, 2019, 2:17:59 AM8/10/19
to golang-nuts
Can you show me how is it possible to do with pkg/errors ? It is not immediately apparent to me. The Causer interface is similar to the Unwrap interface and errors.Cause recursively unwraps an error until it finds one which does not implements the causer interface. So just with errors.WithMessage/Wrap, you can only unwrap an error, but cannot directly compare a nested untyped/unnamed error variable.

Alex

unread,
Aug 10, 2019, 2:38:47 AM8/10/19
to golang-nuts
You are right, I think I mixed it up with some other library, I think it was multierr from hashicorp. Have to check that...

Alex

unread,
Aug 10, 2019, 6:24:17 AM8/10/19
to golang-nuts
But when I think about it maybe it is a better practice to only give a leayer2 error message up a singe layer and handle it there.
And if needed only propagate a layer1 error message futher up.

anderson...@onefootball.com

unread,
Aug 12, 2019, 6:09:48 AM8/12/19
to golang-nuts
Hi Alex,

I see your problem is more like merging errors rather than wrapping them. Indeed I don't see any way you could merge 2 errors with the new `fmt.Errorf`

I believe what you want is something which could hold 2 errors, like

type myError struct {
    msg
string // some additional msg
    current error
// Layer1Error
    cause error // Layer2Error
}

but in that case, you are handling it by yourself, instead of delegating it to fmt.Errorf.

What I see as a possibility would be something like:

var (
   
Layer1Error    = errors.New("some error on layer 1: %w")

   
Layer2Error    = errors.New("some error on layer 2")
)


func callLayer1Function
() error {

    err
:= callLayer2Function()

   
// how to not lose layer2 error but also append a new layer1 error ?
   
// this does not work, since you fully lose layer1 error
   
// with pkg/err

   
return fmt.Errorf(Layer1Error.Error(), Layer1Error)
}


However it makes pointles to have `Layer1Error` as an error, it can be juts a string.

Best,

Anderson
Reply all
Reply to author
Forward
0 new messages