hq-go-errors
is a Go (Golang) package for rich, structured error handling with full stack-trace support, error wrapping, classification, and formatting.
- Full Stack Traces: Capture detailed call stacks at error creation and wrap points, with customizable formatting (e.g., reverse order, separators).
- Error Chaining: Wrap errors to add context while preserving the original stack trace and error details.
- Error Classification: Assign
ErrorType
values to categorize errors for programmatic handling. - Structured Fields: Attach arbitrary key-value metadata (e.g., request IDs, parameters) to errors for enhanced debugging.
- Flexible Formatting: Render errors as human-readable strings or JSON-like maps, with options to include/exclude stack traces, invert chain order, or handle external errors.
- Standards-Compliant: Implements Go’s standard
error
,Unwrap
,Is
, andAs
interfaces, plus additional helpers likeCause
for root cause analysis.
To install hq-go-errors
, run the following command in your Go project:
go get -v -u github.com/hueristiq/hq-go-errors
Make sure your Go environment is set up properly (Go 1.13 or later is recommended).
Use hqgoerrors.New
to create a root error with a full call stack captured at the point of invocation.
package main
import (
"fmt"
hqgoerrors "github.com/hueristiq/hq-go-errors"
)
func main() {
err := hqgoerrors.New("failed to initialize database")
if err != nil {
fmt.Println(err.Error())
}
}
Use hqgoerrors.Wrap
to add context to an existing error, capturing a single stack frame at the wrap point while preserving the original error’s stack trace.
package main
import (
"fmt"
hqgoerrors "github.com/hueristiq/hq-go-errors"
)
func loadConfig() error {
return hqgoerrors.New("cannot read config file")
}
func main() {
if err := loadConfig(); err != nil {
err = hqgoerrors.Wrap(err, "failed to load configuration")
fmt.Println(err.Error())
}
}
You can classify errors and attach structured data:
err := hqgoerrors.New("payment declined",
hqgoerrors.WithType("PaymentError"),
hqgoerrors.WithField("order_id", 1234),
hqgoerrors.WithField("amount", 49.95),
)
-
Retrieve type:
if e, ok := err.(hqgoerrors.Error); ok { fmt.Println("Type:", e.Type()) fmt.Println("Fields:", e.Fields()) }
-
Standard Unwrap:
next := hqgoerrors.Unwrap(err)
-
Deep equality:
if hqgoerrors.Is(err, targetErr) { … }
-
Type assertion:
var myErr *hqgoerrors.Error if hqgoerrors.As(err, &myErr) { fmt.Println("Got:", myErr.Msg) }
-
Root cause:
cause := hqgoerrors.Cause(err)
package main
import (
"fmt"
hqgoerrors "github.com/hueristiq/hq-go-errors"
)
func main() {
err := hqgoerrors.New("root error example!", hqgoerrors.WithType("ERROR_TYPE"), hqgoerrors.WithField("FIELD_KEY_1", "FIELD_VALUE_1"), hqgoerrors.WithField("FIELD_KEY_2", "FIELD_VALUE_2"))
err = hqgoerrors.Wrap(err, "wrap error example 1!")
err = hqgoerrors.Wrap(err, "wrap error example 2!", hqgoerrors.WithType("ERROR_TYPE_2"), hqgoerrors.WithField("FIELD_KEY_1", "FIELD_VALUE_1"), hqgoerrors.WithField("FIELD_KEY_2", "FIELD_VALUE_2"))
formattedStr := hqgoerrors.ToString(err, true)
fmt.Println(formattedStr)
}
output:
[ERROR_TYPE_2] wrap error example 2!
Fields:
FIELD_KEY_1=FIELD_VALUE_1, FIELD_KEY_2=FIELD_VALUE_2
Stack:
main.main:/home/.../hq-go-errors/examples/string_format/main.go:13
wrap error example 1!
Stack:
main.main:/home/.../hq-go-errors/examples/string_format/main.go:12
[ERROR_TYPE] root error example!
Fields:
FIELD_KEY_1=FIELD_VALUE_1, FIELD_KEY_2=FIELD_VALUE_2
Stack:
main.main:/home/.../hq-go-errors/examples/string_format/main.go:13
main.main:/home/.../hq-go-errors/examples/string_format/main.go:12
main.main:/home/.../hq-go-errors/examples/string_format/main.go:10
package main
import (
"encoding/json"
"fmt"
hqgoerrors "github.com/hueristiq/hq-go-errors"
)
func main() {
err := hqgoerrors.New("root error example!", hqgoerrors.WithType("ERROR_TYPE"), hqgoerrors.WithField("FIELD_KEY_1", "FIELD_VALUE_1"), hqgoerrors.WithField("FIELD_KEY_2", "FIELD_VALUE_2"))
err = hqgoerrors.Wrap(err, "wrap error example 1!")
err = hqgoerrors.Wrap(err, "wrap error example 2!", hqgoerrors.WithType("ERROR_TYPE_2"), hqgoerrors.WithField("FIELD_KEY_1", "FIELD_VALUE_1"), hqgoerrors.WithField("FIELD_KEY_2", "FIELD_VALUE_2"))
formattedJSON := hqgoerrors.ToJSON(err, true)
bytes, _ := json.Marshal(formattedJSON)
fmt.Println(string(bytes))
}
output:
{
"root": {
"fields": {
"FIELD_KEY_1": "FIELD_VALUE_1",
"FIELD_KEY_2": "FIELD_VALUE_2"
},
"message": "root error example!",
"stack": [
"main.main:/home/.../hq-go-errors/examples/JSON_format/main.go:14",
"main.main:/home/.../hq-go-errors/examples/JSON_format/main.go:13",
"main.main:/home/.../hq-go-errors/examples/JSON_format/main.go:11"
],
"type": "ERROR_TYPE"
},
"wrap": [
{
"fields": {
"FIELD_KEY_1": "FIELD_VALUE_1",
"FIELD_KEY_2": "FIELD_VALUE_2"
},
"message": "wrap error example 2!",
"stack": "main.main:/home/.../hq-go-errors/examples/JSON_format/main.go:14",
"type": "ERROR_TYPE_2"
},
{
"message": "wrap error example 1!",
"stack": "main.main:/home/.../hq-go-errors/examples/JSON_format/main.go:13"
}
]
}
Contributions are welcome and encouraged! Feel free to submit Pull Requests or report Issues. For more details, check out the contribution guidelines.
A big thank you to all the contributors for your ongoing support!
This package is licensed under the MIT license. You are free to use, modify, and distribute it, as long as you follow the terms of the license. You can find the full license text in the repository - Full MIT license text.