exception

package module
v0.0.17 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 4, 2026 License: MPL-2.0 Imports: 7 Imported by: 2

README

go-exception — Behavior Summary

Module github.com/thanhminhmr/go-exception, Go 1.25, MPL-2.0. Optional zerolog integration (disabled with the no_zerolog build tag).

Core contract

Exception is a sealed interface (exception.go:32): a private __() method prevents any external implementation. All methods use value receivers; mutation methods return a new Exception rather than mutating in place. Documented rule: always use the returned value — never assume the original is unchanged.

The three implementations

The interface has three concrete types, chosen by weight (cheapest sufficient form wins):

String (string.go:38) — lightweight, constant-friendly

type String string. Carries only type + message, parsed by the ": " separator (string.go:15):

  • Missing separator ⇒ whole string is the type, message is empty.
  • Error() drops the separator when type or message is empty (e.g. "IOError: msg""IOError: msg"; ": msg""msg"; "IOError:""IOError").
  • Usable as a const (e.g. const ErrRead = exception.String("IOError: read failed")).
  • All field getters (GetCause, GetSuppressed, GetRecovered, GetStackTrace, GetExtras, GetExtra) return zero values; Clone() returns itself (immutable).
multipleErrors (multiple_errors.go:37) — the join form

type multipleErrors []error. Empty type and message; GetCause() and Unwrap() []error both return itself. Error() is fmt.Sprintf("%v", []error(e)). Produced by Join.

fullException (full_exception.go:18) — the full form

Struct with Type, Message, Cause (multipleErrors), Suppressed (multipleErrors), Recovered any, StackTrace StackFrames, Extras map[string]any. Error() returns "Type: Message", or whichever of type/message is non-empty, or "" if both empty. Unwrap() []error returns Cause.

Promotion-on-enrichment

String and multipleErrors stay cheap until enriched, then promote to fullException carrying over their existing data:

Op on String Op on multipleErrors Result
AddCause (non-empty) AddCause (non-empty) fullException with Cause set, type preserved (String) or causes preserved (multipleErrors)
AddSuppressed (non-empty) AddSuppressed (non-empty) fullException with Suppressed set
SetRecovered(non-nil) SetRecovered(non-nil) fullException with Recovered set
FillStackTrace FillStackTrace fullException with StackTrace set
SetExtras(non-nil) SetExtras(non-nil) fullException with Extras set
SetExtra(key, non-nil) SetExtra(key, non-nil) fullException with a fresh Extras map containing only that key
SetMessage (non-empty) SetMessage (non-empty) String returns a new String; multipleErrors promotes to fullException keeping Cause

Once a fullException, all further mutations stay on fullException (struct is copied and the field is reassigned).

Construction helpers

Join(errors ...error) Exception (multiple_errors.go:25)

Combines errors into a multipleErrors. Nil inputs are filtered. Nested multipleErrors are flattened (auto-unboxed). Returns nil if nothing remains.

Template (template.go)

type Template string. Format(args...) returns String(fmt.Sprintf(t, args...)). Reusable constant format for exception messages.

Panic / Recover (panic.go)
  • PanicError = String("panicked") — the type used for panic-originated exceptions.
  • Panic(v): if v is already an Exception whose type is PanicError, re-panics it unchanged (enables chained recover handlers without altering state). Otherwise wraps v as fullException{Type: "panicked", Recovered: v, StackTrace: StackTrace(1)} and panics it.
  • Recover(callback): no-op when no panic occurred. If the recovered value is an Exception with type PanicError, passes it to the callback unchanged. Otherwise builds a fullException and strips leading runtime.*panic* frames from the trace (panic.go:87-97) so the trace starts at the real panic site. Recover(nil) panics with "BUG: callback is nil". Intended for defer exception.Recover(func(ex exception.Exception) { ... }).

Field semantics

  • Cause (GetCause/AddCause): the root errors that led to this exception. On fullException, AddCause appends via multipleErrors.append (multiple_errors.go:156) which filters nils and flattens nested multipleErrors.
  • Suppressed (GetSuppressed/AddSuppressed): errors intentionally ignored or deferred while handling this exception. Same append semantics as Cause.
  • Recovered (GetRecovered/SetRecovered): the value captured from a panic.
  • StackTrace (GetStackTrace/FillStackTrace): StackFrame{Function, File, Line} slice. FillStackTrace(skip) calls StackTrace(skip+1) so it captures from the caller of FillStackTrace; skip=0 includes that caller. StackTrace captures up to 64 PCs via runtime.Callers(2+skip, …) and appends every frame returned by runtime.CallersFrames.Next, including the last (where more==false).
  • Extras (GetExtras/SetExtras/GetExtra/SetExtra): arbitrary key-value metadata. SetExtra lazily allocates the map on first write; SetExtra(key, nil) deletes the key (no-op on a nil map, since delete on nil is allowed in Go).

errors package integration

  • errors.Is walks the cause chain via multi-Unwrap() []error: fullException.Unwrap() returns Cause, multipleErrors.Unwrap() returns itself. A Join result therefore matches any of its members.
  • errors.As works through the same Unwrap chain.

Clone

  • String.Clone() → returns itself (immutable).
  • multipleErrors.Clone()slices.Clone (new slice, shared error elements).
  • fullException.Clone() → new struct with slices.Clone for Cause/Suppressed/StackTrace and maps.Clone for Extras. Referenced values (error elements, the recovered value) are not deeply cloned — they're shared between original and clone.

Nil handling invariants

  • AddCause/AddSuppressed/Join silently filter all nil error arguments.
  • On String/multipleErrors: SetRecovered(nil), SetExtras(nil), and SetExtra(key, nil) are no-ops returning the receiver unchanged (no promotion).
  • GetExtra on an exception with no extras returns (nil, false).

Zerolog integration (optional)

zerolog.go compiles unless the no_zerolog build tag is set. String, fullException, multipleErrors, StackFrame, and StackFrames all implement MarshalZerologObject / MarshalZerologArray, omitting empty fields and choosing AnErr (single) vs Errs (multiple) based on slice length.

Documentation

Overview

Package exception provides a lightweight exception model for Go.

It supports attaching underlying causes, recording suppressed errors, storing recovered panic values, capturing stack traces, and attaching extras.

Integration with zerolog is optional. It can be disabled with the `no_zerolog` build tag.

Methods that return Exception may either modify the current exception in place or return a new exception instance. Callers should always use the returned value and must not assume that the original exception remains unchanged.

Source code in this package is licensed under the Mozilla Public License 2.0 (MPL-2.0).

Index

Constants

View Source
const PanicError = String("panicked")

PanicError is the default type for exceptions created by Panic and Recover.

Variables

This section is empty.

Functions

func Panic

func Panic(recovered any)

Panic behaves like the built-in panic, but always panics with an Exception whose type is PanicError.

If the value already implements Exception and its type is PanicError, it is re-panicked directly. This allows Panic to be used in a chain of recover handlers without changing the original panic state.

Otherwise, Panic creates a new Exception that uses PanicError as its type, keeps the recovered value, and records the stack trace starting from the caller.

Typical usage together with Recover:

defer exception.Recover(func(recovered exception.Exception) {
    // handle recovered exception
})

if somethingWrong {
    exception.Panic("bad state")
}

func Recover

func Recover(callback func(recovered Exception))

Recover recovers from a panic and passes the recovered value to callback as an Exception.

If no panic occurred, Recover does nothing.

If the recovered value already implements Exception and its type is PanicError, it is passed to callback directly. This allows multiple recover handlers to work together: the first one captures the panic state, and later ones can observe or rethrow the same Exception without modification.

Otherwise, Recover creates a new Exception that uses PanicError as its type, keeps the recovered value, and records the stack trace starting from the location where the panic occurred.

Recover is intended to be used with defer:

defer exception.Recover(func(ex exception.Exception) {
    // handle recovered exception
})

if somethingWrong {
    exception.Panic("bad state")
}

Types

type Exception

type Exception interface {
	// Error returns a string representation of this exception in the form of "Type:
	// Message"
	Error() string

	// GetType returns the type of this exception.
	GetType() string

	// GetMessage returns the message of this exception.
	GetMessage() string

	// SetMessage stores a message inside this exception.
	//
	// Note: This method may modify the current exception or return a new one. Always
	// use the returned [Exception].
	SetMessage(message string, parameters ...any) Exception

	// GetCause returns the list of underlying causes associated with this exception.
	// The slice may be empty if no causes have been specified.
	GetCause() []error

	// AddCause attaches one or more underlying causes to this exception. Causes are
	// typically used to represent the root errors that led to this exception being
	// raised.
	//
	// Note: This method may modify the current exception or return a new one. Always
	// use the returned [Exception].
	AddCause(errors ...error) Exception

	// GetSuppressed returns the list of suppressed errors that were intentionally
	// ignored or deferred while handling this exception. This can be useful when
	// multiple errors occur, but only one is chosen as the primary failure.
	GetSuppressed() []error

	// AddSuppressed attaches one or more suppressed errors to this exception.
	//
	// Note: This method may modify the current exception or return a new one. Always
	// use the returned [Exception].
	AddSuppressed(errors ...error) Exception

	// GetRecovered returns the value captured from a panic recovery, if any. It
	// returns nil if no value was recovered.
	GetRecovered() any

	// SetRecovered stores a recovered panic value inside this exception.
	//
	// Note: This method may modify the current exception or return a new one. Always
	// use the returned [Exception].
	SetRecovered(recovered any) Exception

	// GetStackTrace returns the stack trace captured for this exception, represented
	// as [StackFrames]. The result may be nil if no stack trace was filled.
	GetStackTrace() StackFrames

	// FillStackTrace captures the current call stack starting from the caller of
	// [FillStackTrace] itself and attaches it to this exception.
	//
	// The skip parameter controls how many additional stack frames are omitted. A
	// value of 0 includes the caller of [FillStackTrace], a value of 1 skips that
	// frame, and higher values skip more.
	//
	// Note: This method may modify the current exception or return a new one. Always
	// use the returned [Exception].
	FillStackTrace(skip int) Exception

	// GetExtras returns the additional metadata associated with this exception as a
	// key-value map.
	//
	// Extras can be used to attach arbitrary contextual information such as request
	// identifiers, user information, diagnostic values, or application-specific
	// state.
	//
	// The returned map may be nil if no extras have been set.
	GetExtras() map[string]any

	// SetExtras stores the additional metadata associated with this exception as a
	// key-value map.
	//
	// Extras can be used to attach arbitrary contextual information such as request
	// identifiers, user information, diagnostic values, or application-specific
	// state.
	//
	// Note: This method may modify the current exception or return a new one. Always
	// use the returned [Exception].
	SetExtras(extras map[string]any) Exception

	// GetExtra retrieves a single extra value associated with the given key.
	//
	// Extras can be used to attach arbitrary contextual information such as request
	// identifiers, user information, diagnostic values, or application-specific
	// state.
	//
	// The returned boolean reports whether the key exists.
	GetExtra(key string) (any, bool)

	// SetExtra stores an additional metadata value inside this exception under the
	// specified key.
	//
	// Extras can be used to attach arbitrary contextual information such as request
	// identifiers, user information, diagnostic values, or application-specific
	// state.
	//
	// If a value already exists for the key, it is replaced.
	//
	// Note: This method may modify the current exception or return a new one. Always
	// use the returned [Exception].
	SetExtra(key string, value any) Exception

	// Clone creates an independent copy of this [Exception] and returns it.
	//
	// After cloning, modifications to either [Exception] through its own methods do
	// not affect the other.
	//
	// Note: Referenced values are not deeply cloned and may still be shared.
	Clone() Exception
	// contains filtered or unexported methods
}

Exception defines a lightweight exception model for Go, providing mechanisms for chaining causes, tracking suppressed errors, storing recovered values, capturing stack traces, and attaching extras.

Methods that return Exception may either modify the current exception in place or return a new exception instance. Callers should always use the returned value and must not assume that the original exception remains unchanged.

func Join

func Join(errors ...error) Exception

Join combines multiple errors into a single Exception with an empty type.

Nil values are ignored. If no errors remain, Join returns nil.

If any of the provided errors are Exceptions produced by Join and have not been modified further (other than adding more causes), their causes are automatically unwrapped and merged into the new Exception.

The resulting Exception exposes all non-nil errors, including those from unboxed joins, as its causes. Other details such as the message, suppressed errors, recovered value, and stack trace are left empty.

type StackFrame

type StackFrame struct {
	Function string `json:"function,omitempty"`
	File     string `json:"file,omitempty"`
	Line     int    `json:"line,omitempty"`
}

StackFrame represents a single frame in a stack trace. It contains the function name, source file, and line number for a point in the call stack.

func Function added in v0.0.16

func Function(fn any) (frame StackFrame, ok bool)

func (StackFrame) MarshalZerologObject

func (f StackFrame) MarshalZerologObject(event *zerolog.Event)

MarshalZerologObject marshall this StackFrame as a zerolog object.

type StackFrames

type StackFrames []StackFrame

StackFrames is a slice of StackFrame values. It represents a complete stack trace.

func StackTrace

func StackTrace(skip int) StackFrames

StackTrace captures the current call stack as StackFrames, starting from the caller of StackTrace itself.

The skip parameter controls how many additional stack frames are omitted. A value of 0 includes the caller of StackTrace, a value of 1 skips that frame, and higher values skip more.

func (StackFrames) MarshalZerologArray

func (s StackFrames) MarshalZerologArray(array *zerolog.Array)

MarshalZerologArray marshall this StackFrames as a zerolog array.

type String

type String string

String is a string-based Exception. It behaves like a simple error containing only a type and a message, with no causes, suppressed errors, recovered value, or stack trace.

Type and message are separated by a separator sequence: a colon followed by a space (": "). If the separator is missing, the Exception is considered to have an empty message.

String is often used as a starting point for building a full exception with additional context. When causes, suppressed errors, or stack traces are added, a new Exception will be created that keeps the type and includes the added details:

err := exception.String("IOError: read failed").FillStackTrace(0)

String can also be used as a constant error value, for example:

const ErrRead = exception.String("IOError: read failed")

func (String) AddCause

func (e String) AddCause(errors ...error) Exception

AddCause attaches one or more underlying causes to this exception. Causes are typically used to represent the root errors that led to this exception being raised.

Note: This method may modify the current exception or return a new one. Always use the returned Exception.

func (String) AddSuppressed

func (e String) AddSuppressed(errors ...error) Exception

AddSuppressed attaches one or more suppressed errors to this exception.

Note: This method may modify the current exception or return a new one. Always use the returned Exception.

func (String) Clone added in v0.0.8

func (e String) Clone() Exception

Clone returns an independent copy of this exception.

After cloning, changes made through the methods of one Exception do not affect the other.

Referenced values are not deeply cloned and may still be shared.

func (String) Error

func (e String) Error() string

Error returns a string representation of this exception in the form of "Type: Message"

func (String) FillStackTrace

func (e String) FillStackTrace(skip int) Exception

FillStackTrace captures the current call stack starting from the caller of [FillStackTrace] itself and attaches it to this exception.

The skip parameter controls how many additional stack frames are omitted. A value of 0 includes the caller of [FillStackTrace], a value of 1 skips that frame, and higher values skip more.

Note: This method may modify the current exception or return a new one. Always use the returned Exception.

func (String) GetCause

func (e String) GetCause() []error

GetCause returns the list of underlying causes associated with this exception. The slice may be empty if no causes have been specified.

func (String) GetExtra added in v0.0.10

func (e String) GetExtra(string) (any, bool)

GetExtra retrieves a single extra value associated with the given key.

Extras can be used to attach arbitrary contextual information such as request identifiers, user information, diagnostic values, or application-specific state.

The returned boolean reports whether the key exists.

func (String) GetExtras added in v0.0.10

func (e String) GetExtras() map[string]any

GetExtras returns the additional metadata associated with this exception as a key-value map.

Extras can be used to attach arbitrary contextual information such as request identifiers, user information, diagnostic values, or application-specific state.

The returned map may be nil if no extras have been set.

func (String) GetMessage

func (e String) GetMessage() string

GetMessage returns the message of this exception.

func (String) GetRecovered

func (e String) GetRecovered() any

GetRecovered returns the value captured from a panic recovery, if any. It returns nil if no value was recovered.

func (String) GetStackTrace

func (e String) GetStackTrace() StackFrames

GetStackTrace returns the stack trace captured for this exception, represented as StackFrames. The result may be nil if no stack trace was filled.

func (String) GetSuppressed

func (e String) GetSuppressed() []error

GetSuppressed returns the list of suppressed errors that were intentionally ignored or deferred while handling this exception. This can be useful when multiple errors occur, but only one is chosen as the primary failure.

func (String) GetType

func (e String) GetType() string

GetType returns the type of this exception.

func (String) MarshalZerologObject

func (e String) MarshalZerologObject(event *zerolog.Event)

MarshalZerologObject marshall this Exception as a zerolog object.

func (String) SetExtra added in v0.0.10

func (e String) SetExtra(key string, value any) Exception

SetExtra stores an additional metadata value inside this exception under the specified key.

Extras can be used to attach arbitrary contextual information such as request identifiers, user information, diagnostic values, or application-specific state.

If a value already exists for the key, it is replaced.

Note: This method may modify the current exception or return a new one. Always use the returned Exception.

func (String) SetExtras added in v0.0.10

func (e String) SetExtras(extras map[string]any) Exception

SetExtras stores the additional metadata associated with this exception as a key-value map.

Extras can be used to attach arbitrary contextual information such as request identifiers, user information, diagnostic values, or application-specific state.

Note: This method may modify the current exception or return a new one. Always use the returned Exception.

func (String) SetMessage

func (e String) SetMessage(message string, parameters ...any) Exception

SetMessage stores a message inside this exception.

Note: This method may modify the current exception or return a new one. Always use the returned Exception.

func (String) SetRecovered

func (e String) SetRecovered(recovered any) Exception

SetRecovered stores a recovered panic value inside this exception.

Note: This method may modify the current exception or return a new one. Always use the returned Exception.

type Template added in v0.0.5

type Template string

Template represents a reusable message pattern for creating String exceptions. It behaves like a format string that can be expanded with parameters to produce consistent exception messages.

For example:

const FileIOError = exception.Template("IOError: %s failed")

func (Template) Format added in v0.0.5

func (t Template) Format(parameters ...any) String

Format applies the given parameters to this template using fmt.Sprintf and returns a new String containing the formatted message.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL