diff --git a/util/vizerror/vizerror.go b/util/vizerror/vizerror.go index 158786494..11fe3f3c7 100644 --- a/util/vizerror/vizerror.go +++ b/util/vizerror/vizerror.go @@ -12,7 +12,8 @@ import ( // Error is an error that is safe to display to end users. type Error struct { - err error + err error + internalErr error } // Error implements the error interface. @@ -20,14 +21,21 @@ func (e Error) Error() string { return e.err.Error() } +// InternalError returns the internal error that should not be shown to end +// users. It will be nil if the user-visible error was not constructed using +// WithInternal. +func (e Error) InternalError() error { + return e.internalErr +} + // New returns an error that formats as the given text. It always returns a vizerror.Error. func New(text string) error { - return Error{errors.New(text)} + return Error{err: errors.New(text)} } // Errorf returns an Error with the specified format and values. It always returns a vizerror.Error. func Errorf(format string, a ...any) error { - return Error{fmt.Errorf(format, a...)} + return Error{err: fmt.Errorf(format, a...)} } // Unwrap returns the underlying error. @@ -36,11 +44,15 @@ func (e Error) Unwrap() error { } // Wrap wraps err with a vizerror.Error. +// +// Deprecated: this is almost always the wrong thing to do. Are you really sure +// you know exactly what err.Error() will stringify to and be safe to show to +// users? func Wrap(err error) error { if err == nil { return nil } - return Error{err} + return Error{err: err} } // As returns the first vizerror.Error in err's chain. @@ -48,3 +60,10 @@ func As(err error) (e Error, ok bool) { ok = errors.As(err, &e) return } + +// WithInternal returns a new ErrorWithInternal combining a user-visible error +// string and an internal error to pass around for internal logging but not +// to be shown to end users. +func WithInternal(visibleError string, internalErr error) error { + return Error{err: errors.New(visibleError), internalErr: internalErr} +}