go

Is a recovered panic always of type error?


I would like to return any panic as an error value in a function. Is it safe to assume that all panics are of type error (Example A) or do I need to tread them as strings (Example B) or something else?

Example A: Assuming panics are errors

func API() (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = r.(error)
        }
    }()
    mayPanic()
    return
}

Example B: Treating panics as strings

func API2() (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("%s", r)
        }
    }()
    mayPanic()
    return
}

In my tests both examples create the same output for panics, but there may be cases I am missing.


Solution

  • No. It's the type of whatever argument was supplied to the panic call that you are now recovering. In fact, the return value of recover is the same value supplied to panic.

    Go specs: https://go.dev/ref/spec#Handling_panics

    When the running of deferred functions reaches D [a function D that calls recover], the return value of D's call to recover will be the value passed to the call of panic.

    You can't assume that all recovered values are errors, unless you are in full control of your code and somehow enforce passing errors to panic calls.

    If the code you're dealing with could pass arbitrarily typed values to panic, you should test for its type when recovering it:

    func API() (err error) {
        defer func() {
            if r := recover(); r != nil {
                if err2, ok := r.(error); ok {
                    err = err2 // or some other error wrapping
                } else {
                    err = fmt.Errorf("recovered panic: %v", r)
                }
            }
        }()
        mayPanic()
        return
    }