Error Types

There are various options for declaring errors:

  • errors.New for errors with simple static strings
  • fmt.Errorf for formatted error strings
  • Custom types that implement an Error() method
  • Wrapped errors using "pkg/errors".Wrap When returning errors, consider the following to determine the best choice:

  • Is this a simple error that needs no extra information? If so, errors.Newshould suffice.

  • Do the clients need to detect and handle this error? If so, you should use acustom type, and implement the Error() method.
  • Are you propagating an error returned by a downstream function? If so, checkthe section on error wrapping.
  • Otherwise, fmt.Errorf is okay. If the client needs to detect the error, and you have created a simple errorusing errors.New, use a var for the error.
BadGood
  1. // package foo
  2.  
  3. func Open() error {
  4. return errors.New("could not open")
  5. }
  6.  
  7. // package bar
  8.  
  9. func use() {
  10. if err := foo.Open(); err != nil {
  11. if err.Error() == "could not open" {
  12. // handle
  13. } else {
  14. panic("unknown error")
  15. }
  16. }
  17. }
  1. // package foo
  2.  
  3. var ErrCouldNotOpen = errors.New("could not open")
  4.  
  5. func Open() error {
  6. return ErrCouldNotOpen
  7. }
  8.  
  9. // package bar
  10.  
  11. if err := foo.Open(); err != nil {
  12. if err == foo.ErrCouldNotOpen {
  13. // handle
  14. } else {
  15. panic("unknown error")
  16. }
  17. }

If you have an error that clients may need to detect, and you would like to addmore information to it (e.g., it is not a static string), then you should use acustom type.

BadGood
  1. func open(file string) error {
  2. return fmt.Errorf("file %q not found", file)
  3. }
  4.  
  5. func use() {
  6. if err := open(); err != nil {
  7. if strings.Contains(err.Error(), "not found") {
  8. // handle
  9. } else {
  10. panic("unknown error")
  11. }
  12. }
  13. }
  1. type errNotFound struct {
  2. file string
  3. }
  4.  
  5. func (e errNotFound) Error() string {
  6. return fmt.Sprintf("file %q not found", e.file)
  7. }
  8.  
  9. func open(file string) error {
  10. return errNotFound{file: file}
  11. }
  12.  
  13. func use() {
  14. if err := open(); err != nil {
  15. if _, ok := err.(errNotFound); ok {
  16. // handle
  17. } else {
  18. panic("unknown error")
  19. }
  20. }
  21. }

Be careful with exporting custom error types directly since they become part ofthe public API of the package. It is preferable to expose matcher functions tocheck the error instead.

  1. // package foo
  2.  
  3. type errNotFound struct {
  4. file string
  5. }
  6.  
  7. func (e errNotFound) Error() string {
  8. return fmt.Sprintf("file %q not found", e.file)
  9. }
  10.  
  11. func IsNotFoundError(err error) bool {
  12. _, ok := err.(errNotFound)
  13. return ok
  14. }
  15.  
  16. func Open(file string) error {
  17. return errNotFound{file: file}
  18. }
  19.  
  20. // package bar
  21.  
  22. if err := foo.Open("foo"); err != nil {
  23. if foo.IsNotFoundError(err) {
  24. // handle
  25. } else {
  26. panic("unknown error")
  27. }
  28. }