How to Create Error Types with Stack Traces in Go

Create a custom error type in Go that captures the call stack using runtime.Callers to include stack traces in error messages.

Create a custom error type that embeds error and includes a stack trace field, then use runtime.Callers to capture the stack when instantiating it.

package main

import (
	"errors"
	"fmt"
	"runtime"
	"strings"
)

type StackError struct {
	msg  string
	stack []uintptr
}

func (e *StackError) Error() string {
	return e.msg
}

func (e *StackError) Stack() string {
	var sb strings.Builder
	sb.WriteString("Stack trace:\n")
	for _, pc := range e.stack {
		frames := runtime.CallersFrames([]uintptr{pc})
		for {
			frame, more := frames.Next()
			sb.Printf("%s:%d\n", frame.File, frame.Line)
			if !more {
				break
			}
		}
	}
	return sb.String()
}

func NewStackError(msg string) error {
	const depth = 32
	var pcs [depth]uintptr
	n := runtime.Callers(2, pcs[:])
	return &StackError{msg: msg, stack: pcs[:n]}
}

func main() {
	err := NewStackError("something went wrong")
	if err != nil {
		fmt.Println(err)
		if se, ok := err.(*StackError); ok {
			fmt.Println(se.Stack())
		}
	}
}