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 // }
141 package multierr // import "go.uber.org/multierr"
142 143 import (
144 "bytes"
145 "errors"
146 "fmt"
147 "io"
148 "strings"
149 "sync"
150 "sync/atomic"
151 )
152 153 var (
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.
177 var _bufferPool = sync.Pool{
178 New: func() interface{} {
179 return &bytes.Buffer{}
180 },
181 }
182 183 type 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.
197 func 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.
208 type 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.
216 func (merr *multiError) Errors() []error {
217 if merr == nil {
218 return nil
219 }
220 return merr.errors
221 }
222 223 func (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.
240 func 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 249 func (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 257 func (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 269 func (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.
279 func 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 298 type 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.
315 func 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.
339 func 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))
413 func 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].
435 func 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 // }
495 func 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].
516 type 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.
542 type Invoke func() error
543 544 // Invoke calls the supplied function and returns its result.
545 func (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.
568 func 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.
625 func 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 // }
644 func AppendFunc(into *error, fn func() error) {
645 AppendInvoke(into, Invoke(fn))
646 }
647