Errors

See the errors package documentation and wiki page for a detailed explanation of why it is an idiomatic way to assert errors. The https://codeberg.org/fillmore-labs/errortype package is used to enforce this policy in the CI.

Error sentinel

If a package-level variable is a sentinel representing the error:

var ErrNotExist = errors.New("resource does not exist")

An assertion on that error must be:

if errors.Is(err, ErrNotExist) ...

Error types

If an error is a type that is instantiated when the error is created because it contains details about the error such as:

type FundingNotExistError struct {
	Repo *repo_model.Repository
}

An assertion on an error of that type must be:

import 	"forgejo.org/modules/util"

if util.ErrIsType[FundingNotExistError](err) ...

Why not use errors.AsType?

Because it is not an assertion, it converts the error. Using it in an expression is inconvenient because the returned error must be discarded. Consider this example:

if util.ErrIsType[FundingNotExistError](err) || util.ErrIsType[FundingCorruptedError](err) {
  return
}

versus:

if _, ok := errors.AsType[FundingNotExistError](err); ok {
  return
}

if _, ok := errors.AsType[FundingCorruptedError](err); ok {
  return
}

Why not use an assertion function?

Because it is frequently implemented incorrectly. For instance the following does not walk the error tree:

func IsErrSourceNotExist(err error) bool {
	_, ok := err.(ErrSourceNotExist)
	return ok
}

will return true on:

IsErrSourceNotExist(ErrSourceNotExist{})

but will return false on:

IsErrSourceNotExist(fmt.Errorf("Wrong: %w", ErrSourceNotExist{}))

The correct implementation would be:

func IsErrSourceNotExist(err error) bool {
	_, ok := errors.AsType[ErrSourceNotExist](err)
	return ok
}