1// Copyright (c) 2017-2023 Uber Technologies, Inc.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19// THE SOFTWARE.
20
21// Package multierr allows combining one or more errors together.
22//
23// # Overview
24//
25// Errors can be combined with the use of the Combine function.
26//
27// multierr.Combine(
28// reader.Close(),
29// writer.Close(),
30// conn.Close(),
31// )
32//
33// If only two errors are being combined, the Append function may be used
34// instead.
35//
36// err = multierr.Append(reader.Close(), writer.Close())
37//
38// The underlying list of errors for a returned error object may be retrieved
39// with the Errors function.
40//
41// errors := multierr.Errors(err)
42// if len(errors) > 0 {
43// fmt.Println("The following errors occurred:", errors)
44// }
45//
46// # Appending from a loop
47//
48// You sometimes need to append into an error from a loop.
49//
50// var err error
51// for _, item := range items {
52// err = multierr.Append(err, process(item))
53// }
54//
55// Cases like this may require knowledge of whether an individual instance
56// failed. This usually requires introduction of a new variable.
57//
58// var err error
59// for _, item := range items {
60// if perr := process(item); perr != nil {
61// log.Warn("skipping item", item)
62// err = multierr.Append(err, perr)
63// }
64// }
65//
66// multierr includes AppendInto to simplify cases like this.
67//
68// var err error
69// for _, item := range items {
70// if multierr.AppendInto(&err, process(item)) {
71// log.Warn("skipping item", item)
72// }
73// }
74//
75// This will append the error into the err variable, and return true if that
76// individual error was non-nil.
77//
78// See [AppendInto] for more information.
79//
80// # Deferred Functions
81//
82// Go makes it possible to modify the return value of a function in a defer
83// block if the function was using named returns. This makes it possible to
84// record resource cleanup failures from deferred blocks.
85//
86// func sendRequest(req Request) (err error) {
87// conn, err := openConnection()
88// if err != nil {
89// return err
90// }
91// defer func() {
92// err = multierr.Append(err, conn.Close())
93// }()
94// // ...
95// }
96//
97// multierr provides the Invoker type and AppendInvoke function to make cases
98// like the above simpler and obviate the need for a closure. The following is
99// roughly equivalent to the example above.
100//
101// func sendRequest(req Request) (err error) {
102// conn, err := openConnection()
103// if err != nil {
104// return err
105// }
106// defer multierr.AppendInvoke(&err, multierr.Close(conn))
107// // ...
108// }
109//
110// See [AppendInvoke] and [Invoker] for more information.
111//
112// NOTE: If you're modifying an error from inside a defer, you MUST use a named
113// return value for that function.
114//
115// # Advanced Usage
116//
117// Errors returned by Combine and Append MAY implement the following
118// interface.
119//
120// type errorGroup interface {
121// // Returns a slice containing the underlying list of errors.
122// //
123// // This slice MUST NOT be modified by the caller.
124// Errors() []error
125// }
126//
127// Note that if you need access to list of errors behind a multierr error, you
128// should prefer using the Errors function. That said, if you need cheap
129// read-only access to the underlying errors slice, you can attempt to cast
130// the error to this interface. You MUST handle the failure case gracefully
131// because errors returned by Combine and Append are not guaranteed to
132// implement this interface.
133//
134// var errors []error
135// group, ok := err.(errorGroup)
136// if ok {
137// errors = group.Errors()
138// } else {
139// errors = []error{err}
140// }
141package multierr // import "go.uber.org/multierr"
142
143import (
144 "bytes"
145 "errors"
146 "fmt"
147 "io"
148 "strings"
149 "sync"
150 "sync/atomic"
151)
152
153var (
154 // Separator for single-line error messages.
155 _singlelineSeparator = []byte("; ")
156
157 // Prefix for multi-line messages
158 _multilinePrefix = []byte("the following errors occurred:")
159
160 // Prefix for the first and following lines of an item in a list of
161 // multi-line error messages.
162 //
163 // For example, if a single item is:
164 //
165 // foo
166 // bar
167 //
168 // It will become,
169 //
170 // - foo
171 // bar
172 _multilineSeparator = []byte("\n - ")
173 _multilineIndent = []byte(" ")
174)
175
176// _bufferPool is a pool of bytes.Buffers.
177var _bufferPool = sync.Pool{
178 New: func() interface{} {
179 return &bytes.Buffer{}
180 },
181}
182
183type errorGroup interface {
184 Errors() []error
185}
186
187// Errors returns a slice containing zero or more errors that the supplied
188// error is composed of. If the error is nil, a nil slice is returned.
189//
190// err := multierr.Append(r.Close(), w.Close())
191// errors := multierr.Errors(err)
192//
193// If the error is not composed of other errors, the returned slice contains
194// just the error that was passed in.
195//
196// Callers of this function are free to modify the returned slice.
197func Errors(err error) []error {
198 return extractErrors(err)
199}
200
201// multiError is an error that holds one or more errors.
202//
203// An instance of this is guaranteed to be non-empty and flattened. That is,
204// none of the errors inside multiError are other multiErrors.
205//
206// multiError formats to a semi-colon delimited list of error messages with
207// %v and with a more readable multi-line format with %+v.
208type multiError struct {
209 copyNeeded atomic.Bool
210 errors []error
211}
212
213// Errors returns the list of underlying errors.
214//
215// This slice MUST NOT be modified.
216func (merr *multiError) Errors() []error {
217 if merr == nil {
218 return nil
219 }
220 return merr.errors
221}
222
223func (merr *multiError) Error() string {
224 if merr == nil {
225 return ""
226 }
227
228 buff := _bufferPool.Get().(*bytes.Buffer)
229 buff.Reset()
230
231 merr.writeSingleline(buff)
232
233 result := buff.String()
234 _bufferPool.Put(buff)
235 return result
236}
237
238// Every compares every error in the given err against the given target error
239// using [errors.Is], and returns true only if every comparison returned true.
240func Every(err error, target error) bool {
241 for _, e := range extractErrors(err) {
242 if !errors.Is(e, target) {
243 return false
244 }
245 }
246 return true
247}
248
249func (merr *multiError) Format(f fmt.State, c rune) {
250 if c == 'v' && f.Flag('+') {
251 merr.writeMultiline(f)
252 } else {
253 merr.writeSingleline(f)
254 }
255}
256
257func (merr *multiError) writeSingleline(w io.Writer) {
258 first := true
259 for _, item := range merr.errors {
260 if first {
261 first = false
262 } else {
263 w.Write(_singlelineSeparator)
264 }
265 io.WriteString(w, item.Error())
266 }
267}
268
269func (merr *multiError) writeMultiline(w io.Writer) {
270 w.Write(_multilinePrefix)
271 for _, item := range merr.errors {
272 w.Write(_multilineSeparator)
273 writePrefixLine(w, _multilineIndent, fmt.Sprintf("%+v", item))
274 }
275}
276
277// Writes s to the writer with the given prefix added before each line after
278// the first.
279func writePrefixLine(w io.Writer, prefix []byte, s string) {
280 first := true
281 for len(s) > 0 {
282 if first {
283 first = false
284 } else {
285 w.Write(prefix)
286 }
287
288 idx := strings.IndexByte(s, '\n')
289 if idx < 0 {
290 idx = len(s) - 1
291 }
292
293 io.WriteString(w, s[:idx+1])
294 s = s[idx+1:]
295 }
296}
297
298type inspectResult struct {
299 // Number of top-level non-nil errors
300 Count int
301
302 // Total number of errors including multiErrors
303 Capacity int
304
305 // Index of the first non-nil error in the list. Value is meaningless if
306 // Count is zero.
307 FirstErrorIdx int
308
309 // Whether the list contains at least one multiError
310 ContainsMultiError bool
311}
312
313// Inspects the given slice of errors so that we can efficiently allocate
314// space for it.
315func inspect(errors []error) (res inspectResult) {
316 first := true
317 for i, err := range errors {
318 if err == nil {
319 continue
320 }
321
322 res.Count++
323 if first {
324 first = false
325 res.FirstErrorIdx = i
326 }
327
328 if merr, ok := err.(*multiError); ok {
329 res.Capacity += len(merr.errors)
330 res.ContainsMultiError = true
331 } else {
332 res.Capacity++
333 }
334 }
335 return
336}
337
338// fromSlice converts the given list of errors into a single error.
339func fromSlice(errors []error) error {
340 // Don't pay to inspect small slices.
341 switch len(errors) {
342 case 0:
343 return nil
344 case 1:
345 return errors[0]
346 }
347
348 res := inspect(errors)
349 switch res.Count {
350 case 0:
351 return nil
352 case 1:
353 // only one non-nil entry
354 return errors[res.FirstErrorIdx]
355 case len(errors):
356 if !res.ContainsMultiError {
357 // Error list is flat. Make a copy of it
358 // Otherwise "errors" escapes to the heap
359 // unconditionally for all other cases.
360 // This lets us optimize for the "no errors" case.
361 out := append(([]error)(nil), errors...)
362 return &multiError{errors: out}
363 }
364 }
365
366 nonNilErrs := make([]error, 0, res.Capacity)
367 for _, err := range errors[res.FirstErrorIdx:] {
368 if err == nil {
369 continue
370 }
371
372 if nested, ok := err.(*multiError); ok {
373 nonNilErrs = append(nonNilErrs, nested.errors...)
374 } else {
375 nonNilErrs = append(nonNilErrs, err)
376 }
377 }
378
379 return &multiError{errors: nonNilErrs}
380}
381
382// Combine combines the passed errors into a single error.
383//
384// If zero arguments were passed or if all items are nil, a nil error is
385// returned.
386//
387// Combine(nil, nil) // == nil
388//
389// If only a single error was passed, it is returned as-is.
390//
391// Combine(err) // == err
392//
393// Combine skips over nil arguments so this function may be used to combine
394// together errors from operations that fail independently of each other.
395//
396// multierr.Combine(
397// reader.Close(),
398// writer.Close(),
399// pipe.Close(),
400// )
401//
402// If any of the passed errors is a multierr error, it will be flattened along
403// with the other errors.
404//
405// multierr.Combine(multierr.Combine(err1, err2), err3)
406// // is the same as
407// multierr.Combine(err1, err2, err3)
408//
409// The returned error formats into a readable multi-line error message if
410// formatted with %+v.
411//
412// fmt.Sprintf("%+v", multierr.Combine(err1, err2))
413func Combine(errors ...error) error {
414 return fromSlice(errors)
415}
416
417// Append appends the given errors together. Either value may be nil.
418//
419// This function is a specialization of Combine for the common case where
420// there are only two errors.
421//
422// err = multierr.Append(reader.Close(), writer.Close())
423//
424// The following pattern may also be used to record failure of deferred
425// operations without losing information about the original error.
426//
427// func doSomething(..) (err error) {
428// f := acquireResource()
429// defer func() {
430// err = multierr.Append(err, f.Close())
431// }()
432//
433// Note that the variable MUST be a named return to append an error to it from
434// the defer statement. See also [AppendInvoke].
435func Append(left error, right error) error {
436 switch {
437 case left == nil:
438 return right
439 case right == nil:
440 return left
441 }
442
443 if _, ok := right.(*multiError); !ok {
444 if l, ok := left.(*multiError); ok && !l.copyNeeded.Swap(true) {
445 // Common case where the error on the left is constantly being
446 // appended to.
447 errs := append(l.errors, right)
448 return &multiError{errors: errs}
449 } else if !ok {
450 // Both errors are single errors.
451 return &multiError{errors: []error{left, right}}
452 }
453 }
454
455 // Either right or both, left and right, are multiErrors. Rely on usual
456 // expensive logic.
457 errors := [2]error{left, right}
458 return fromSlice(errors[0:])
459}
460
461// AppendInto appends an error into the destination of an error pointer and
462// returns whether the error being appended was non-nil.
463//
464// var err error
465// multierr.AppendInto(&err, r.Close())
466// multierr.AppendInto(&err, w.Close())
467//
468// The above is equivalent to,
469//
470// err := multierr.Append(r.Close(), w.Close())
471//
472// As AppendInto reports whether the provided error was non-nil, it may be
473// used to build a multierr error in a loop more ergonomically. For example:
474//
475// var err error
476// for line := range lines {
477// var item Item
478// if multierr.AppendInto(&err, parse(line, &item)) {
479// continue
480// }
481// items = append(items, item)
482// }
483//
484// Compare this with a version that relies solely on Append:
485//
486// var err error
487// for line := range lines {
488// var item Item
489// if parseErr := parse(line, &item); parseErr != nil {
490// err = multierr.Append(err, parseErr)
491// continue
492// }
493// items = append(items, item)
494// }
495func AppendInto(into *error, err error) (errored bool) {
496 if into == nil {
497 // We panic if 'into' is nil. This is not documented above
498 // because suggesting that the pointer must be non-nil may
499 // confuse users into thinking that the error that it points
500 // to must be non-nil.
501 panic("misuse of multierr.AppendInto: into pointer must not be nil")
502 }
503
504 if err == nil {
505 return false
506 }
507 *into = Append(*into, err)
508 return true
509}
510
511// Invoker is an operation that may fail with an error. Use it with
512// AppendInvoke to append the result of calling the function into an error.
513// This allows you to conveniently defer capture of failing operations.
514//
515// See also, [Close] and [Invoke].
516type Invoker interface {
517 Invoke() error
518}
519
520// Invoke wraps a function which may fail with an error to match the Invoker
521// interface. Use it to supply functions matching this signature to
522// AppendInvoke.
523//
524// For example,
525//
526// func processReader(r io.Reader) (err error) {
527// scanner := bufio.NewScanner(r)
528// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
529// for scanner.Scan() {
530// // ...
531// }
532// // ...
533// }
534//
535// In this example, the following line will construct the Invoker right away,
536// but defer the invocation of scanner.Err() until the function returns.
537//
538// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
539//
540// Note that the error you're appending to from the defer statement MUST be a
541// named return.
542type Invoke func() error
543
544// Invoke calls the supplied function and returns its result.
545func (i Invoke) Invoke() error { return i() }
546
547// Close builds an Invoker that closes the provided io.Closer. Use it with
548// AppendInvoke to close io.Closers and append their results into an error.
549//
550// For example,
551//
552// func processFile(path string) (err error) {
553// f, err := os.Open(path)
554// if err != nil {
555// return err
556// }
557// defer multierr.AppendInvoke(&err, multierr.Close(f))
558// return processReader(f)
559// }
560//
561// In this example, multierr.Close will construct the Invoker right away, but
562// defer the invocation of f.Close until the function returns.
563//
564// defer multierr.AppendInvoke(&err, multierr.Close(f))
565//
566// Note that the error you're appending to from the defer statement MUST be a
567// named return.
568func Close(closer io.Closer) Invoker {
569 return Invoke(closer.Close)
570}
571
572// AppendInvoke appends the result of calling the given Invoker into the
573// provided error pointer. Use it with named returns to safely defer
574// invocation of fallible operations until a function returns, and capture the
575// resulting errors.
576//
577// func doSomething(...) (err error) {
578// // ...
579// f, err := openFile(..)
580// if err != nil {
581// return err
582// }
583//
584// // multierr will call f.Close() when this function returns and
585// // if the operation fails, its append its error into the
586// // returned error.
587// defer multierr.AppendInvoke(&err, multierr.Close(f))
588//
589// scanner := bufio.NewScanner(f)
590// // Similarly, this scheduled scanner.Err to be called and
591// // inspected when the function returns and append its error
592// // into the returned error.
593// defer multierr.AppendInvoke(&err, multierr.Invoke(scanner.Err))
594//
595// // ...
596// }
597//
598// NOTE: If used with a defer, the error variable MUST be a named return.
599//
600// Without defer, AppendInvoke behaves exactly like AppendInto.
601//
602// err := // ...
603// multierr.AppendInvoke(&err, mutltierr.Invoke(foo))
604//
605// // ...is roughly equivalent to...
606//
607// err := // ...
608// multierr.AppendInto(&err, foo())
609//
610// The advantage of the indirection introduced by Invoker is to make it easy
611// to defer the invocation of a function. Without this indirection, the
612// invoked function will be evaluated at the time of the defer block rather
613// than when the function returns.
614//
615// // BAD: This is likely not what the caller intended. This will evaluate
616// // foo() right away and append its result into the error when the
617// // function returns.
618// defer multierr.AppendInto(&err, foo())
619//
620// // GOOD: This will defer invocation of foo unutil the function returns.
621// defer multierr.AppendInvoke(&err, multierr.Invoke(foo))
622//
623// multierr provides a few Invoker implementations out of the box for
624// convenience. See [Invoker] for more information.
625func AppendInvoke(into *error, invoker Invoker) {
626 AppendInto(into, invoker.Invoke())
627}
628
629// AppendFunc is a shorthand for [AppendInvoke].
630// It allows using function or method value directly
631// without having to wrap it into an [Invoker] interface.
632//
633// func doSomething(...) (err error) {
634// w, err := startWorker(...)
635// if err != nil {
636// return err
637// }
638//
639// // multierr will call w.Stop() when this function returns and
640// // if the operation fails, it appends its error into the
641// // returned error.
642// defer multierr.AppendFunc(&err, w.Stop)
643// }
644func AppendFunc(into *error, fn func() error) {
645 AppendInvoke(into, Invoke(fn))
646}