premise: Errors are values. Unless some fundamental constraint has been violated, in which case feel free to panic, errors are just some other state to be handled.

pain points: as far as I can tell, there are 2 main complaints:


For the first issue, people usually propose some syntax sugar to shorten if err != nil { ... }

How about a slightly more generic solution that checks against the zero value, then we can coopt the short circuiting behaviour of && to make the common case one line. A new unary operator !! evaluates to true if the operand is the zero value of its type, else false.

_, err = f() // currently valid
err != nil && return _, _, fmt.Errorf("foo: %w", err)

_, err = g() // proposed, pre
!!err && return _, _, fmt.Errorf("bar: %w", err)

_, err = h() // proposed, post
err!! && return _, _, fmt.Errorf("fizz: %w", err)

// equivalent function
func z(v interface{}) bool {
       return reflect.ValueOf(v).IsZero() // but that reflect penalty...

_, err = i()
z(err) && return _, _, fmt.Errorf("buzz: %w", err)

Also really want _ to mean the zero value for any type...

I also thought the idea of assigning to an error handler was a good start, though it does have some issues, like implicit passing of arguments, handler having to be defined in function scope, and non local returns, I guess with some extra thought this is where the check/handle came from.

func f() (err error) {
        handler := func(other arg, err error) {
                if err != nil {
                        err = fmt.Errorf("some extra context: %w", err)
                        return // non local return???

        var o arg

        _, handler(o) = g()

func handler(err error, arg string) {
        if err != nil {


Usually the people focusing in the second camp want sum types, forcing you to handle errors. People usually deal with this now with linters, and to be honest, I don't really think this is as much of a concern