Declarative error handling for Go.

Build Status Go Report Card Documentation

ErrorFlow

Declarative error handling for Go.

Motivation

Reading list:

ErrorFlow goal is to provide a library solution to the issues raised in articles above.

Library solution (as opposed to a language change), although less clean, has a very important benefit: it is optional. Many language proposals for addressing this issue have been rejected because language change is required to be universally applicable. Library solution can be used only for use cases where it works well.

Features

  • 'err'-variable-free type-safe branchless business logic
reader := errf.Io.CheckReadCloser(os.Open(srcFilename))
	/* vs */
reader, err := os.Open(srcFilename)
if err != nil {
	return err
}
  • Declarative multiple errors handling logic
defer errf.IfError().ReturnFirst().ThenAssignTo(&err)
defer errf.CheckDeferErr(writer.Close)
	/* vs */
defer func() {
	closeErr := writer.Close()
	if closeErr != nil and err == nil {
		err = closeErr
	}
}()
  • Declarative errors logging logic
defer errf.IfError().LogIfSuppressed().ThenAssignTo(&err)
defer errf.CheckDeferErr(writer.Close)
	/* vs */
defer func() {
	closeErr := writer.Close()
	if closeErr != nil {
		if err == nil {
			err = closeErr
		} else {
			log.Printf("error closing writer: %w", err)
		}
	}
}()
  • Doesn't affect APIs
    • Every use of ErrorFlow is scoped to a single function and doesn't leak into its API
  • Extendable
    • Custom return types for type safety
    • Custom ErrorFlow config functions (e.g. creating a wrapper that converts errors from a third-party libraries into standard error types for an internal codebase)

Example: error handling for a file gzip function

Error handling requirements for function:

  • Returns error only in case of error that affects result file correctness.
  • Cleans up dst file in case of the error instead of leaving it in inconsistent state.
  • Logs all internal errors that it didn't return.
  • Wraps returned errors with "error compressing file: " prefix.
  • Performs input parameters validation.

ErrorFlow style error handling

func GzipFile(dstFilename string, srcFilename string) (err error) {
	// defer IfError()... creates and configures
	// ErrorFlow error handler for this function.
	// When any of Check* functions encounters non-nil error
	// it immediately sends error to this handler
	// unwinding all stacked defers.
	errWrapper := errf.WrapperFmtErrorw("error compressing file")
	defer errf.IfError().ReturnFirst().LogIfSuppressed().Apply(errWrapper).ThenAssignTo(&err)

	errf.CheckAssert(len(dstFilename) > 0, "dst file should be specified")
	errf.CheckAssert(len(srcFilename) > 0, "src file should be specified")

	reader := errf.Io.CheckReadCloser(os.Open(srcFilename))
	defer errf.With(errWrapper).LogDefer(reader.Close)

	writer := errf.Io.CheckWriteCloser(os.Create(dstFilename))
	defer errf.Handle().OnAnyErrOrPanic(func() { os.Remove(dstFilename) })
	defer errf.CheckDeferErr(writer.Close)

	gzipWriter := gzip.NewWriter(writer)
	defer errf.CheckDeferErr(gzipWriter.Close)

	return errf.CheckDiscard(io.Copy(gzipWriter, reader)).IfOkReturnNil
}

Compare with

Plain Go implementation without any error handling
func GzipFile(dstFilename string, srcFilename string) error {
	reader, _ := os.Open(srcFilename)
	defer reader.Close()

	writer, _ := os.Create(dstFilename)
	defer writer.Close()

	gzipWriter := gzip.NewWriter(writer)
	defer gzipWriter.Close()

	_, _ = io.Copy(gzipWriter, reader)

	return nil
}
Plain Go implementation (functionally roughly equivalent to ErrorFlow example above; not using helper functions)
func GzipFile(dstFilename string, srcFilename string) (err error) {
	if len(dstFilename) == 0 {
		return fmt.Errorf("error compressing file: dst file should be specified")
	}
	if len(srcFilename) == 0 {
		return fmt.Errorf("error compressing file: src file should be specified")
	}

	reader, err := os.Open(srcFilename)
	if err != nil {
		return fmt.Errorf("error compressing file: %w", err)
	}
	defer func() {
		closeErr := reader.Close()
		if closeErr != nil {
			log.Println(closeErr)
		}
	}()

	writer, err := os.Create(dstFilename)
	if err != nil {
		return fmt.Errorf("error compressing file: %w", err)
	}
	defer func() {
		if err != nil {
			os.Remove(dstFilename)
		}
	}()
	defer func() {
		closeErr := writer.Close()
		if closeErr != nil {
			if err == nil {
				err = fmt.Errorf("error compressing file: %w", closeErr)
			} else {
				log.Println(fmt.Errorf("[suppressed] error compressing file: %w", closeErr))
			}
		}
	}()

	gzipWriter := gzip.NewWriter(writer)
	defer func() {
		closeErr := gzipWriter.Close()
		if closeErr != nil {
			if err == nil {
				err = fmt.Errorf("error compressing file: %w", closeErr)
			} else {
				log.Println(fmt.Errorf("[suppressed] error compressing file: %w", closeErr))
			}
		}
	}()

	_, err = io.Copy(gzipWriter, reader)
	if err != nil {
		return fmt.Errorf("error compressing file: %w", err)
	}

	return nil
}
ErrorFlow-Lite style error handling (using only defer helper functions, but not IfError/Check* handler)
func GzipFile(dstFilename string, srcFilename string) (err error) {
	errflow := errf.With(
		errf.LogStrategyIfSuppressed,
		errf.WrapperFmtErrorw("error compressing file"),
	)

	if len(dstFilename) == 0 {
		return fmt.Errorf("error compressing file: dst file should be specified")
	}
	if len(srcFilename) == 0 {
		return fmt.Errorf("error compressing file: src file should be specified")
	}

	reader, err := os.Open(srcFilename)
	if err != nil {
		return fmt.Errorf("error compressing file: %w", err)
	}
	defer errflow.LogDefer(reader.Close)

	writer, err := os.Create(dstFilename)
	if err != nil {
		return fmt.Errorf("error compressing file: %w", err)
	}
	defer func() {
		if err != nil {
			os.Remove(dstFilename)
		}
	}()
	defer errflow.IfErrorAssignTo(&err, writer.Close)

	gzipWriter := gzip.NewWriter(writer)
	defer errflow.IfErrorAssignTo(&err, gzipWriter.Close)

	_, err = io.Copy(gzipWriter, reader)
	if err != nil {
		return fmt.Errorf("error compressing file: %w", err)
	}

	return nil
}
Similar Resources

Wraps the normal error and provides an error that is easy to use with net/http.

Go HTTP Error Wraps the normal error and provides an error that is easy to use with net/http. Install go get -u github.com/cateiru/go-http-error Usage

Dec 20, 2021

Reduce debugging time while programming Go. Use static and stack-trace analysis to determine which func call causes the error.

Reduce debugging time while programming Go. Use static and stack-trace analysis to determine which func call causes the error.

Errlog: reduce debugging time while programming Introduction Use errlog to improve error logging and speed up debugging while you create amazing code

Nov 18, 2022

A drop-in replacement for Go errors, with some added sugar! Unwrap user-friendly messages, HTTP status code, easy wrapping with multiple error types.

A drop-in replacement for Go errors, with some added sugar! Unwrap user-friendly messages, HTTP status code, easy wrapping with multiple error types.

Errors Errors package is a drop-in replacement of the built-in Go errors package with no external dependencies. It lets you create errors of 11 differ

Dec 6, 2022

A Go (golang) package for representing a list of errors as a single error.

go-multierror go-multierror is a package for Go that provides a mechanism for representing a list of error values as a single error. This allows a fun

Jan 1, 2023

Error tracing and annotation.

errors -- import "github.com/juju/errgo" The errors package provides a way to create and diagnose errors. It is compatible with the usual Go error idi

Nov 3, 2022

A flexible error support library for Go

errors Please see http://godoc.org/github.com/spacemonkeygo/errors for info License Copyright (C) 2014 Space Monkey, Inc. Licensed under the Apache Li

Nov 3, 2022

A powerful, custom error package for Go

custom-error-go A powerful, custom error package for Go Detailed explanation: https://medium.com/codealchemist/error-handling-in-go-made-more-powerful

Apr 19, 2022

Go extract error codes

go-extract-error-codes Overview This library helps to extract possible error codes from configured go-restful applications quality level: PoC High Lev

Nov 24, 2021

Error interface wrappers for Google's errdetails protobuf types, because they're handy as heck and I want to use them more

Error interface wrappers for Google's errdetails protobuf types, because they're handy as heck and I want to use them more

Nov 18, 2021
Related tags
A comprehensive error handling library for Go

Highlights The errorx library provides error implementation and error-related utilities. Library features include (but are not limited to): Stack trac

Jan 6, 2023
Generic error handling with panic, recover, and defer.

Generic error handling with panic, recover, and defer.

Aug 25, 2022
brief: a piece of error handling codelet

brief a piece of error handling codelet. this code only demonstrates how to hide sql.ErrNoRows to the caller. the Get() method defined in the pkg/proj

Oct 30, 2021
Just another error handling primitives for golang

errors Just another error handling primitives for golang Install go install github.com/WAY29/errors@latest Usage New error and print error context Th

Feb 19, 2022
🥷 CError (Custom Error Handling)

?? CError (Custom Error Handling) Installation Via go packages: go get github.com/rozturac/cerror Usage Here is a sample CError uses: import ( "gi

Sep 21, 2022
Errors - A lib for handling error gracefully in Go

?? Errors Errors 是一个用于优雅地处理 Go 中错误的库。 Read me in English ??‍ 功能特性 优雅地处理 error,嗯,

Jan 17, 2022
Simple, intuitive and effective error handling for Go

Error Handling with eluv-io/errors-go The package eluv-io/errors-go makes Go error handling simple, intuitive and effective. err := someFunctionThatCa

Jan 19, 2022
Error handling hook & helper function to simplify writing API handler methods in Go.

Error handling hook & helper function to simplify writing API handler methods in Go.

Jan 19, 2022
Try - Idiomatic monadic-ish error handling for go

Try Idiomatic monadic-ish error handling for go. Examples import

Jan 24, 2022
Go error library with error portability over the network

cockroachdb/errors: Go errors with network portability This library aims to be used as a drop-in replacement to github.com/pkg/errors and Go's standar

Dec 29, 2022