@@ -1016,7 +1016,7 @@ type prettyErrorFormatter struct {
1016
1016
// format formats the error to the console. This error should be human
1017
1017
// readable.
1018
1018
func (p * prettyErrorFormatter ) format (err error ) {
1019
- output , _ := cliHumanFormatError (err , & formatOpts {
1019
+ output , _ := cliHumanFormatError ("" , err , & formatOpts {
1020
1020
Verbose : p .verbose ,
1021
1021
})
1022
1022
// always trail with a newline
@@ -1041,7 +1041,7 @@ const indent = " "
1041
1041
// go run main.go exp example-error cmd
1042
1042
// go run main.go exp example-error multi-error
1043
1043
// go run main.go exp example-error validation
1044
- func cliHumanFormatError (err error , opts * formatOpts ) (string , bool ) {
1044
+ func cliHumanFormatError (from string , err error , opts * formatOpts ) (string , bool ) {
1045
1045
if opts == nil {
1046
1046
opts = & formatOpts {}
1047
1047
}
@@ -1053,28 +1053,26 @@ func cliHumanFormatError(err error, opts *formatOpts) (string, bool) {
1053
1053
multiErrors := multi .Unwrap ()
1054
1054
if len (multiErrors ) == 1 {
1055
1055
// Format as a single error
1056
- return cliHumanFormatError (multiErrors [0 ], opts )
1056
+ return cliHumanFormatError (from , multiErrors [0 ], opts )
1057
1057
}
1058
- return formatMultiError (multiErrors , opts ), true
1058
+ return formatMultiError (from , multiErrors , opts ), true
1059
1059
}
1060
1060
1061
1061
// First check for sentinel errors that we want to handle specially.
1062
1062
// Order does matter! We want to check for the most specific errors first.
1063
- //var sdkError *codersdk.Error
1064
- //if errors.As(err, &sdkError) {
1065
1063
if sdkError , ok := err .(* codersdk.Error ); ok {
1066
- return formatCoderSDKError (sdkError , opts ), true
1064
+ return formatCoderSDKError (from , sdkError , opts ), true
1067
1065
}
1068
1066
1069
- //var cmdErr *clibase.RunCommandError
1070
- //if errors.As(err, &cmdErr) {
1071
1067
if cmdErr , ok := err .(* clibase.RunCommandError ); ok {
1068
+ // no need to pass the "from" context to this since it is always
1069
+ // top level. We care about what is below this.
1072
1070
return formatRunCommandError (cmdErr , opts ), true
1073
1071
}
1074
1072
1075
1073
uw , ok := err .(interface { Unwrap () error })
1076
1074
if ok {
1077
- msg , special := cliHumanFormatError (uw .Unwrap (), opts )
1075
+ msg , special := cliHumanFormatError (from + traceError ( err ), uw .Unwrap (), opts )
1078
1076
if special {
1079
1077
return msg , special
1080
1078
}
@@ -1100,16 +1098,20 @@ func cliHumanFormatError(err error, opts *formatOpts) (string, bool) {
1100
1098
// <verbose error message>
1101
1099
// 2. <heading error message>
1102
1100
// <verbose error message>
1103
- func formatMultiError (multi []error , opts * formatOpts ) string {
1101
+ func formatMultiError (from string , multi []error , opts * formatOpts ) string {
1104
1102
var errorStrings []string
1105
1103
for _ , err := range multi {
1106
- msg , _ := cliHumanFormatError (err , opts )
1104
+ msg , _ := cliHumanFormatError ("" , err , opts )
1107
1105
errorStrings = append (errorStrings , msg )
1108
1106
}
1109
1107
1110
1108
// Write errors out
1111
1109
var str strings.Builder
1112
- _ , _ = str .WriteString (pretty .Sprint (headLineStyle (), fmt .Sprintf ("%d errors encountered:" , len (multi ))))
1110
+ var traceMsg string
1111
+ if opts .Verbose {
1112
+ traceMsg = fmt .Sprintf ("Trace=[%s])" , from )
1113
+ }
1114
+ _ , _ = str .WriteString (pretty .Sprint (headLineStyle (), fmt .Sprintf ("%d errors encountered: %s" , len (multi ), traceMsg )))
1113
1115
for i , errStr := range errorStrings {
1114
1116
// Indent each error
1115
1117
errStr = strings .ReplaceAll (errStr , "\n " , "\n " + indent )
@@ -1138,7 +1140,7 @@ func formatRunCommandError(err *clibase.RunCommandError, opts *formatOpts) strin
1138
1140
var str strings.Builder
1139
1141
_ , _ = str .WriteString (pretty .Sprint (headLineStyle (), fmt .Sprintf ("Encountered an error running %q" , err .Cmd .FullName ())))
1140
1142
1141
- msgString , special := cliHumanFormatError (err .Err , opts )
1143
+ msgString , special := cliHumanFormatError ("" , err .Err , opts )
1142
1144
_ , _ = str .WriteString ("\n " )
1143
1145
if special {
1144
1146
_ , _ = str .WriteString (msgString )
@@ -1151,11 +1153,15 @@ func formatRunCommandError(err *clibase.RunCommandError, opts *formatOpts) strin
1151
1153
1152
1154
// formatCoderSDKError come from API requests. In verbose mode, add the
1153
1155
// request debug information.
1154
- func formatCoderSDKError (err * codersdk.Error , opts * formatOpts ) string {
1156
+ func formatCoderSDKError (from string , err * codersdk.Error , opts * formatOpts ) string {
1155
1157
var str strings.Builder
1156
1158
if opts .Verbose {
1157
1159
_ , _ = str .WriteString (pretty .Sprint (headLineStyle (), fmt .Sprintf ("API request error to \" %s:%s\" . Status code %d" , err .Method (), err .URL (), err .StatusCode ())))
1158
1160
_ , _ = str .WriteString ("\n " )
1161
+ if from != "" {
1162
+ _ , _ = str .WriteString (pretty .Sprint (headLineStyle (), fmt .Sprintf ("Trace=[%s]" , from )))
1163
+ _ , _ = str .WriteString ("\n " )
1164
+ }
1159
1165
}
1160
1166
1161
1167
_ , _ = str .WriteString (pretty .Sprint (headLineStyle (), err .Message ))
@@ -1171,6 +1177,19 @@ func formatCoderSDKError(err *codersdk.Error, opts *formatOpts) string {
1171
1177
return str .String ()
1172
1178
}
1173
1179
1180
+ // traceError is a helper function that aides developers debugging failed cli
1181
+ // commands. When we pretty print errors, we lose the context in which they came.
1182
+ // This function adds the context back. Unfortunately there is no easy way to get
1183
+ // the prefix to: "error string: %w", so we do a bit of string manipulation.
1184
+ func traceError (err error ) string {
1185
+ if uw , ok := err .(interface { Unwrap () error }); ok {
1186
+ a , b := err .Error (), uw .Unwrap ().Error ()
1187
+ c := strings .TrimSuffix (a , b )
1188
+ return c
1189
+ }
1190
+ return err .Error ()
1191
+ }
1192
+
1174
1193
// These styles are arbitrary.
1175
1194
func headLineStyle () pretty.Style {
1176
1195
return cliui .DefaultStyles .Error
0 commit comments