Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Decorating Go Error
package main
import (
func main() {
fmt.Println(ResourceNotFound("1234", "User", nil).Error())
type Error interface {
// Generic error interface
Code() string
Message() string
Cause() error
Causes() []error
Data() map[string]interface{}
String() string
ResponseErrType() ResponseErrType
SetResponseType(r ResponseErrType) Error
Component() ErrComponent
SetComponent(c ErrComponent) Error
Retryable() bool
SetRetryable() Error
AppendCause() Error
type GoError struct {
code string
message string
data map[string]interface{}
causes []error
component ErrComponent
responseType ResponseErrType
retryable bool
appendCause bool
type ErrComponent string
const (
ErrService ErrComponent = "service"
ErrRepo ErrComponent = "repository"
ErrLib ErrComponent = "library"
type ResponseErrType string
const (
BadRequest ResponseErrType = "BadRequest"
Forbidden ResponseErrType = "Forbidden"
NotFound ResponseErrType = "NotFound"
AlreadyExists ResponseErrType = "AlreadyExists"
func NewGoError(code string, data map[string]interface{}, cause error) Error {
return &GoError{
code: code,
data: data,
causes: []error{cause},
func (e *GoError) Error() string {
s := fmt.Sprintf("%s:%s", e.code, e.Message())
if e.appendCause {
s += getCauses(e.causes)
return s
func (e *GoError) Code() string {
return e.code
func (e *GoError) Message() string {
return localize(e.code, templates[e.code],
func (e *GoError) Cause() error {
if len(e.causes) > 1 {
return e.causes[0]
return nil
func (e *GoError) Causes() []error {
return e.causes
func (e *GoError) Data() map[string]interface{} {
func (e *GoError) String() string {
return e.Error()
func (e *GoError) ResponseErrType() ResponseErrType {
return e.responseType
func (e *GoError) SetResponseType(r ResponseErrType) Error {
e.responseType = r
return e
func (e *GoError) Component() ErrComponent {
return e.component
func (e *GoError) SetComponent(c ErrComponent) Error {
e.component = c
return e
func (e *GoError) Retryable() bool {
return e.retryable
func (e *GoError) SetRetryable() Error {
e.retryable = true
return e
// will append the error cause to the err string
func (e *GoError) AppendCause() Error {
e.appendCause = true
return e
func getCauses(errors []error) string {
var s strings.Builder
for _, err := range errors {
s.WriteString("; ")
return s.String()
// i18N bundle file can be used to templatizing the error string
var templates = map[string]string{
"ResourceNotFound": "Resource of type {{.kind}} '{{.id}}' is not found",
func localize(id, template string, data map[string]interface{}) string {
bundle := i18n.NewBundle(language.English)
localizer := i18n.NewLocalizer(bundle, "en")
return localizer.MustLocalize(&i18n.LocalizeConfig{
DefaultMessage: &i18n.Message{
ID: id,
Other: template,
TemplateData: data,
// error abstraction
func ResourceNotFound(id, kind string, cause error) Error {
data := map[string]interface{}{"kind": kind, "id": id}
return NewGoError("ResourceNotFound", data, cause).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment