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