errors
                                
                                 errors copied to clipboard
                                
                                    errors copied to clipboard
                            
                            
                            
                        WithMessage appends message after associated stack traces instead of before
After amending an error using errors.WithMessage, printing the error with %+v prints the extra information after the stack trace instead of with the original message. For example:
package main
import (
	"log"
	"github.com/pkg/errors"
)
func a() error {
	return errors.New("error details")
}
func b() error {
	if err := a(); err != nil {
		return errors.WithMessage(err, "additional details")
	}
	return nil
}
func main() {
	if err := b(); err != nil {
		log.Fatalf("%+v", err)
	}
}
This prints:
2017/02/06 14:34:13 error details
main.a
	.../example.go:10
main.b
	.../example.go:14
main.main
	.../example.go:21
runtime.main
	/usr/local/Cellar/go/1.7.4_2/libexec/src/runtime/proc.go:183
runtime.goexit
	/usr/local/Cellar/go/1.7.4_2/libexec/src/runtime/asm_amd64.s:2086
additional details
exit status 1
As mentioned in #75, it seems like it would be more helpful to include the additional information with original error message. When using %v with the same example, it prints messages in this way now:
2017/02/06 14:33:32 additional details: error details
exit status 1
I can confirm the issue. I also expected the full error message at the top.
Sorry, that's how it's designed, the inner most error goes on the top.
On Wed, 14 Jun 2017, 18:06 benma [email protected] wrote:
I can confirm the issue. I also expected the full error message at the top.
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/pkg/errors/issues/102#issuecomment-308354212, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAcA74NBfxiMYyvC0UMmEO6GKFM5Y9vks5sD5SFgaJpZM4L4z5i .
Ok, thanks.
For reference, if anyone finds it useful, this is the function I use to format the stack trace. It puts the full error message on top and removes the quadratic blowup of the stack trace when there are many wraps (see https://github.com/pkg/errors/issues/75#issuecomment-242394764):
func ErrFormat(err error) string {
	if err == nil {
		return ""
	}
	type stackTracer interface {
		StackTrace() errors.StackTrace
	}
	cause := errors.Cause(err)
	if stackTrace, ok := cause.(stackTracer); ok {
		buf := bytes.Buffer{}
		for _, frame := range stackTrace.StackTrace() {
			buf.WriteString(fmt.Sprintf("\n%+v", frame))
		}
		return err.Error() + buf.String()
	}
	return err.Error()
}
This is AWESOME!
It's precisely what I wanted for users of this package.
There is no way that I could choose one format that would fit everyone's needs, so I wanted to provide the raw materials so you could define your own format.
On Wed, Jun 14, 2017 at 7:12 PM, benma [email protected] wrote:
For reference, if anyone finds it useful, this is the function I use to format the stack trace. It puts the full error message on top and removes the quadratic blowup of the stack trace when there are many wraps (see #75 (comment) https://github.com/pkg/errors/issues/75#issuecomment-242394764 ):
func ErrFormat(err error) string { if err == nil { return "" } type stackTracer interface { StackTrace() errors.StackTrace } cause := errors.Cause(err) if stackTrace, ok := cause.(stackTracer); ok { buf := bytes.Buffer{} for _, frame := range stackTrace.StackTrace() { buf.WriteString(fmt.Sprintf("%+v\n", frame)) } return err.Error() + "\n" + buf.String() } return err.Error() }
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/pkg/errors/issues/102#issuecomment-308372569, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAcA0-M8PkycAQPLYHhDkjECqIYmn8Fks5sD6QHgaJpZM4L4z5i .
I am glad you did ;)
Maybe it would be worth it to include a few different formatters in the library so new users don't get any surprises and don't need to spend time doing their own? So far we've identified at least one different one where multiple users have been surprised before, and likely will again, so we could save their time dealing with this.
I don't want to add helper formatter functions because they will begat more and more increasingly undifferentiated formatters.
However, I'm happy to take PRs that add examples of how to write your own formatter.
On Wed, Jun 14, 2017 at 8:50 PM, benma [email protected] wrote:
I am glad you did ;)
Maybe it would be worth it to include a few different formatters in the library so new users don't have get any surprises and don't need to spend time doing their own? So far we've identified at least one different one where multiple users have been surprised before, and likely will again, so we could save their time dealing with this.
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/pkg/errors/issues/102#issuecomment-308395761, or mute the thread https://github.com/notifications/unsubscribe-auth/AAAcA0DZMyuz7CAVAQnrqFn1W4c6rgnhks5sD7sPgaJpZM4L4z5i .
I understand. Thanks!
Here's a formatter I just finished...
func printError(w io.Writer, err error) {
	// Messages come back from deepest to shallowest; reverse the order for printing.
	msgs, stack := deconstruct(err)
	for i := range msgs {
		if i > 0 {
			fmt.Fprint(w, "\n...caused by ")
		}
		fmt.Fprint(w, msgs[len(msgs)-i-1])
	}
	if stack != nil {
		fmt.Fprintf(w, "%+v\n", stack)
	}
}
func deconstruct(err error) ([]string, errors.StackTrace) {
	if err == nil {
		return nil, nil
	}
	myMsg := err.Error()
	var messages []string
	var stack errors.StackTrace
	type causer interface {
		Cause() error
	}
	if causer, ok := err.(causer); ok {
		messages, stack = deconstruct(causer.Cause())
		if len(messages) > 0 {
			pos := strings.Index(myMsg, ": "+messages[len(messages)-1])
			if pos >= 0 {
				myMsg = myMsg[:pos]
			}
		}
	}
	messages = append(messages, myMsg)
	if stack == nil {
		type stackTracer interface {
			StackTrace() errors.StackTrace
		}
		if tracer, ok := err.(stackTracer); ok {
			stack = tracer.StackTrace()
		}
	}
	return messages, stack
}
Example output:
You do not appear to be inside of a git repo, this tool is meant to be used in a git repo
...caused by Failed to execute 'git rev-parse --show-toplevel'
...caused by fatal: Not a git repository (or any of the parent directories): .git
main.(*gitter).Command
	/Users/scottb/src/mn/projects/fullstory/go/src/fs/transform/main/repoman/git.go:151
main.run
	/Users/scottb/src/mn/projects/fullstory/go/src/fs/transform/main/repoman/repoman.go:224
main.main
	/Users/scottb/src/mn/projects/fullstory/go/src/fs/transform/main/repoman/repoman.go:111
runtime.main
	/usr/local/Cellar/go/1.7.4/libexec/src/runtime/proc.go:183
runtime.goexit
	/usr/local/Cellar/go/1.7.4/libexec/src/runtime/asm_amd64.s:2086