github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/object-api-errors.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "context" 22 "errors" 23 "fmt" 24 "io" 25 ) 26 27 // Converts underlying storage error. Convenience function written to 28 // handle all cases where we have known types of errors returned by 29 // underlying storage layer. 30 func toObjectErr(err error, params ...string) error { 31 if err == nil { 32 return nil 33 } 34 35 // Unwarp the error first 36 err = unwrapAll(err) 37 38 if err == context.Canceled { 39 return context.Canceled 40 } 41 42 switch err.Error() { 43 case errVolumeNotFound.Error(): 44 apiErr := BucketNotFound{} 45 if len(params) >= 1 { 46 apiErr.Bucket = params[0] 47 } 48 return apiErr 49 case errVolumeNotEmpty.Error(): 50 apiErr := BucketNotEmpty{} 51 if len(params) >= 1 { 52 apiErr.Bucket = params[0] 53 } 54 return apiErr 55 case errVolumeExists.Error(): 56 apiErr := BucketExists{} 57 if len(params) >= 1 { 58 apiErr.Bucket = params[0] 59 } 60 return apiErr 61 case errDiskFull.Error(): 62 return StorageFull{} 63 case errTooManyOpenFiles.Error(): 64 return SlowDown{} 65 case errFileAccessDenied.Error(): 66 apiErr := PrefixAccessDenied{} 67 if len(params) >= 1 { 68 apiErr.Bucket = params[0] 69 } 70 if len(params) >= 2 { 71 apiErr.Object = decodeDirObject(params[1]) 72 } 73 return apiErr 74 case errIsNotRegular.Error(): 75 apiErr := ObjectExistsAsDirectory{} 76 if len(params) >= 1 { 77 apiErr.Bucket = params[0] 78 } 79 if len(params) >= 2 { 80 apiErr.Object = decodeDirObject(params[1]) 81 } 82 return apiErr 83 case errFileVersionNotFound.Error(): 84 apiErr := VersionNotFound{} 85 if len(params) >= 1 { 86 apiErr.Bucket = params[0] 87 } 88 if len(params) >= 2 { 89 apiErr.Object = decodeDirObject(params[1]) 90 } 91 if len(params) >= 3 { 92 apiErr.VersionID = params[2] 93 } 94 return apiErr 95 case errMethodNotAllowed.Error(): 96 apiErr := MethodNotAllowed{} 97 if len(params) >= 1 { 98 apiErr.Bucket = params[0] 99 } 100 if len(params) >= 2 { 101 apiErr.Object = decodeDirObject(params[1]) 102 } 103 return apiErr 104 case errFileNotFound.Error(): 105 apiErr := ObjectNotFound{} 106 if len(params) >= 1 { 107 apiErr.Bucket = params[0] 108 } 109 if len(params) >= 2 { 110 apiErr.Object = decodeDirObject(params[1]) 111 } 112 return apiErr 113 case errUploadIDNotFound.Error(): 114 apiErr := InvalidUploadID{} 115 if len(params) >= 1 { 116 apiErr.Bucket = params[0] 117 } 118 if len(params) >= 2 { 119 apiErr.Object = decodeDirObject(params[1]) 120 } 121 if len(params) >= 3 { 122 apiErr.UploadID = params[2] 123 } 124 return apiErr 125 case errFileNameTooLong.Error(): 126 apiErr := ObjectNameInvalid{} 127 if len(params) >= 1 { 128 apiErr.Bucket = params[0] 129 } 130 if len(params) >= 2 { 131 apiErr.Object = decodeDirObject(params[1]) 132 } 133 return apiErr 134 case errDataTooLarge.Error(): 135 apiErr := ObjectTooLarge{} 136 if len(params) >= 1 { 137 apiErr.Bucket = params[0] 138 } 139 if len(params) >= 2 { 140 apiErr.Object = decodeDirObject(params[1]) 141 } 142 return apiErr 143 case errDataTooSmall.Error(): 144 apiErr := ObjectTooSmall{} 145 if len(params) >= 1 { 146 apiErr.Bucket = params[0] 147 } 148 if len(params) >= 2 { 149 apiErr.Object = decodeDirObject(params[1]) 150 } 151 return apiErr 152 case errErasureReadQuorum.Error(): 153 apiErr := InsufficientReadQuorum{} 154 if len(params) >= 1 { 155 apiErr.Bucket = params[0] 156 } 157 if len(params) >= 2 { 158 apiErr.Object = decodeDirObject(params[1]) 159 } 160 return apiErr 161 case errErasureWriteQuorum.Error(): 162 apiErr := InsufficientWriteQuorum{} 163 if len(params) >= 1 { 164 apiErr.Bucket = params[0] 165 } 166 if len(params) >= 2 { 167 apiErr.Object = decodeDirObject(params[1]) 168 } 169 return apiErr 170 case io.ErrUnexpectedEOF.Error(), io.ErrShortWrite.Error(), context.Canceled.Error(), context.DeadlineExceeded.Error(): 171 apiErr := IncompleteBody{} 172 if len(params) >= 1 { 173 apiErr.Bucket = params[0] 174 } 175 if len(params) >= 2 { 176 apiErr.Object = decodeDirObject(params[1]) 177 } 178 return apiErr 179 } 180 return err 181 } 182 183 // SignatureDoesNotMatch - when content md5 does not match with what was sent from client. 184 type SignatureDoesNotMatch struct{} 185 186 func (e SignatureDoesNotMatch) Error() string { 187 return "The request signature we calculated does not match the signature you provided. Check your key and signing method." 188 } 189 190 // StorageFull storage ran out of space. 191 type StorageFull struct{} 192 193 func (e StorageFull) Error() string { 194 return "Storage reached its minimum free drive threshold." 195 } 196 197 // SlowDown too many file descriptors open or backend busy . 198 type SlowDown struct{} 199 200 func (e SlowDown) Error() string { 201 return "Please reduce your request rate" 202 } 203 204 // InsufficientReadQuorum storage cannot satisfy quorum for read operation. 205 type InsufficientReadQuorum GenericError 206 207 func (e InsufficientReadQuorum) Error() string { 208 return "Storage resources are insufficient for the read operation " + e.Bucket + "/" + e.Object 209 } 210 211 // Unwrap the error. 212 func (e InsufficientReadQuorum) Unwrap() error { 213 return errErasureReadQuorum 214 } 215 216 // InsufficientWriteQuorum storage cannot satisfy quorum for write operation. 217 type InsufficientWriteQuorum GenericError 218 219 func (e InsufficientWriteQuorum) Error() string { 220 return "Storage resources are insufficient for the write operation " + e.Bucket + "/" + e.Object 221 } 222 223 // Unwrap the error. 224 func (e InsufficientWriteQuorum) Unwrap() error { 225 return errErasureWriteQuorum 226 } 227 228 // GenericError - generic object layer error. 229 type GenericError struct { 230 Bucket string 231 Object string 232 VersionID string 233 Err error 234 } 235 236 // Unwrap the error to its underlying error. 237 func (e GenericError) Unwrap() error { 238 return e.Err 239 } 240 241 // InvalidArgument incorrect input argument 242 type InvalidArgument GenericError 243 244 func (e InvalidArgument) Error() string { 245 if e.Err != nil { 246 return "Invalid arguments provided for " + e.Bucket + "/" + e.Object + ": (" + e.Err.Error() + ")" 247 } 248 return "Invalid arguments provided for " + e.Bucket + "/" + e.Object 249 } 250 251 // BucketNotFound bucket does not exist. 252 type BucketNotFound GenericError 253 254 func (e BucketNotFound) Error() string { 255 return "Bucket not found: " + e.Bucket 256 } 257 258 // BucketAlreadyExists the requested bucket name is not available. 259 type BucketAlreadyExists GenericError 260 261 func (e BucketAlreadyExists) Error() string { 262 return "The requested bucket name is not available. The bucket namespace is shared by all users of the system. Please select a different name and try again." 263 } 264 265 // BucketAlreadyOwnedByYou already owned by you. 266 type BucketAlreadyOwnedByYou GenericError 267 268 func (e BucketAlreadyOwnedByYou) Error() string { 269 return "Bucket already owned by you: " + e.Bucket 270 } 271 272 // BucketNotEmpty bucket is not empty. 273 type BucketNotEmpty GenericError 274 275 func (e BucketNotEmpty) Error() string { 276 return "Bucket not empty: " + e.Bucket 277 } 278 279 // InvalidVersionID invalid version id 280 type InvalidVersionID GenericError 281 282 func (e InvalidVersionID) Error() string { 283 return "Invalid version id: " + e.Bucket + "/" + e.Object + "(" + e.VersionID + ")" 284 } 285 286 // VersionNotFound version does not exist. 287 type VersionNotFound GenericError 288 289 func (e VersionNotFound) Error() string { 290 return "Version not found: " + e.Bucket + "/" + e.Object + "(" + e.VersionID + ")" 291 } 292 293 // ObjectNotFound object does not exist. 294 type ObjectNotFound GenericError 295 296 func (e ObjectNotFound) Error() string { 297 return "Object not found: " + e.Bucket + "/" + e.Object 298 } 299 300 // MethodNotAllowed on the object 301 type MethodNotAllowed GenericError 302 303 func (e MethodNotAllowed) Error() string { 304 return "Method not allowed: " + e.Bucket + "/" + e.Object 305 } 306 307 // ObjectLocked object is currently WORM protected. 308 type ObjectLocked GenericError 309 310 func (e ObjectLocked) Error() string { 311 return "Object is WORM protected and cannot be overwritten: " + e.Bucket + "/" + e.Object + "(" + e.VersionID + ")" 312 } 313 314 // ObjectAlreadyExists object already exists. 315 type ObjectAlreadyExists GenericError 316 317 func (e ObjectAlreadyExists) Error() string { 318 return "Object: " + e.Bucket + "/" + e.Object + " already exists" 319 } 320 321 // ObjectExistsAsDirectory object already exists as a directory. 322 type ObjectExistsAsDirectory GenericError 323 324 func (e ObjectExistsAsDirectory) Error() string { 325 return "Object exists on : " + e.Bucket + " as directory " + e.Object 326 } 327 328 // PrefixAccessDenied object access is denied. 329 type PrefixAccessDenied GenericError 330 331 func (e PrefixAccessDenied) Error() string { 332 return "Prefix access is denied: " + e.Bucket + SlashSeparator + e.Object 333 } 334 335 // BucketExists bucket exists. 336 type BucketExists GenericError 337 338 func (e BucketExists) Error() string { 339 return "Bucket exists: " + e.Bucket 340 } 341 342 // InvalidUploadIDKeyCombination - invalid upload id and key marker combination. 343 type InvalidUploadIDKeyCombination struct { 344 UploadIDMarker, KeyMarker string 345 } 346 347 func (e InvalidUploadIDKeyCombination) Error() string { 348 return fmt.Sprintf("Invalid combination of uploadID marker '%s' and marker '%s'", e.UploadIDMarker, e.KeyMarker) 349 } 350 351 // BucketPolicyNotFound - no bucket policy found. 352 type BucketPolicyNotFound GenericError 353 354 func (e BucketPolicyNotFound) Error() string { 355 return "No bucket policy configuration found for bucket: " + e.Bucket 356 } 357 358 // BucketLifecycleNotFound - no bucket lifecycle found. 359 type BucketLifecycleNotFound GenericError 360 361 func (e BucketLifecycleNotFound) Error() string { 362 return "No bucket lifecycle configuration found for bucket : " + e.Bucket 363 } 364 365 // BucketSSEConfigNotFound - no bucket encryption found 366 type BucketSSEConfigNotFound GenericError 367 368 func (e BucketSSEConfigNotFound) Error() string { 369 return "No bucket encryption configuration found for bucket: " + e.Bucket 370 } 371 372 // BucketTaggingNotFound - no bucket tags found 373 type BucketTaggingNotFound GenericError 374 375 func (e BucketTaggingNotFound) Error() string { 376 return "No bucket tags found for bucket: " + e.Bucket 377 } 378 379 // BucketObjectLockConfigNotFound - no bucket object lock config found 380 type BucketObjectLockConfigNotFound GenericError 381 382 func (e BucketObjectLockConfigNotFound) Error() string { 383 return "No bucket object lock configuration found for bucket: " + e.Bucket 384 } 385 386 // BucketQuotaConfigNotFound - no bucket quota config found. 387 type BucketQuotaConfigNotFound GenericError 388 389 func (e BucketQuotaConfigNotFound) Error() string { 390 return "No quota config found for bucket : " + e.Bucket 391 } 392 393 // BucketQuotaExceeded - bucket quota exceeded. 394 type BucketQuotaExceeded GenericError 395 396 func (e BucketQuotaExceeded) Error() string { 397 return "Bucket quota exceeded for bucket: " + e.Bucket 398 } 399 400 // BucketReplicationConfigNotFound - no bucket replication config found 401 type BucketReplicationConfigNotFound GenericError 402 403 func (e BucketReplicationConfigNotFound) Error() string { 404 return "The replication configuration was not found: " + e.Bucket 405 } 406 407 // BucketRemoteDestinationNotFound bucket does not exist. 408 type BucketRemoteDestinationNotFound GenericError 409 410 func (e BucketRemoteDestinationNotFound) Error() string { 411 return "Destination bucket does not exist: " + e.Bucket 412 } 413 414 // BucketRemoteTargetNotFound remote target does not exist. 415 type BucketRemoteTargetNotFound GenericError 416 417 func (e BucketRemoteTargetNotFound) Error() string { 418 return "Remote target not found: " + e.Bucket 419 } 420 421 // RemoteTargetConnectionErr remote target connection failure. 422 type RemoteTargetConnectionErr struct { 423 Err error 424 Bucket string 425 Endpoint string 426 AccessKey string 427 } 428 429 func (e RemoteTargetConnectionErr) Error() string { 430 if e.Bucket != "" { 431 return fmt.Sprintf("Remote service endpoint offline, target bucket: %s or remote service credentials: %s invalid \n\t%s", e.Bucket, e.AccessKey, e.Err.Error()) 432 } 433 return fmt.Sprintf("Remote service endpoint %s not available\n\t%s", e.Endpoint, e.Err.Error()) 434 } 435 436 // BucketRemoteIdenticalToSource remote already exists for this target type. 437 type BucketRemoteIdenticalToSource struct { 438 GenericError 439 Endpoint string 440 } 441 442 func (e BucketRemoteIdenticalToSource) Error() string { 443 return fmt.Sprintf("Remote service endpoint %s is self referential to current cluster", e.Endpoint) 444 } 445 446 // BucketRemoteAlreadyExists remote already exists for this target type. 447 type BucketRemoteAlreadyExists GenericError 448 449 func (e BucketRemoteAlreadyExists) Error() string { 450 return "Remote already exists for this bucket: " + e.Bucket 451 } 452 453 // BucketRemoteLabelInUse remote already exists for this target label. 454 type BucketRemoteLabelInUse GenericError 455 456 func (e BucketRemoteLabelInUse) Error() string { 457 return "Remote with this label already exists for this bucket: " + e.Bucket 458 } 459 460 // BucketRemoteArnTypeInvalid arn type for remote is not valid. 461 type BucketRemoteArnTypeInvalid GenericError 462 463 func (e BucketRemoteArnTypeInvalid) Error() string { 464 return "Remote ARN type not valid: " + e.Bucket 465 } 466 467 // BucketRemoteArnInvalid arn needs to be specified. 468 type BucketRemoteArnInvalid GenericError 469 470 func (e BucketRemoteArnInvalid) Error() string { 471 return "Remote ARN has invalid format: " + e.Bucket 472 } 473 474 // BucketRemoteRemoveDisallowed when replication configuration exists 475 type BucketRemoteRemoveDisallowed GenericError 476 477 func (e BucketRemoteRemoveDisallowed) Error() string { 478 return "Replication configuration exists with this ARN:" + e.Bucket 479 } 480 481 // BucketRemoteTargetNotVersioned remote target does not have versioning enabled. 482 type BucketRemoteTargetNotVersioned GenericError 483 484 func (e BucketRemoteTargetNotVersioned) Error() string { 485 return "Remote target does not have versioning enabled: " + e.Bucket 486 } 487 488 // BucketReplicationSourceNotVersioned replication source does not have versioning enabled. 489 type BucketReplicationSourceNotVersioned GenericError 490 491 func (e BucketReplicationSourceNotVersioned) Error() string { 492 return "Replication source does not have versioning enabled: " + e.Bucket 493 } 494 495 // TransitionStorageClassNotFound remote tier not configured. 496 type TransitionStorageClassNotFound GenericError 497 498 func (e TransitionStorageClassNotFound) Error() string { 499 return "Transition storage class not found " 500 } 501 502 // InvalidObjectState restore-object doesn't apply for the current state of the object. 503 type InvalidObjectState GenericError 504 505 func (e InvalidObjectState) Error() string { 506 return "The operation is not valid for the current state of the object " + e.Bucket + "/" + e.Object + "(" + e.VersionID + ")" 507 } 508 509 // Bucket related errors. 510 511 // BucketNameInvalid - bucketname provided is invalid. 512 type BucketNameInvalid GenericError 513 514 // Error returns string an error formatted as the given text. 515 func (e BucketNameInvalid) Error() string { 516 return "Bucket name invalid: " + e.Bucket 517 } 518 519 // Object related errors. 520 521 // ObjectNameInvalid - object name provided is invalid. 522 type ObjectNameInvalid GenericError 523 524 // ObjectNameTooLong - object name too long. 525 type ObjectNameTooLong GenericError 526 527 // ObjectNamePrefixAsSlash - object name has a slash as prefix. 528 type ObjectNamePrefixAsSlash GenericError 529 530 // Error returns string an error formatted as the given text. 531 func (e ObjectNameInvalid) Error() string { 532 return "Object name invalid: " + e.Bucket + "/" + e.Object 533 } 534 535 // Error returns string an error formatted as the given text. 536 func (e ObjectNameTooLong) Error() string { 537 return "Object name too long: " + e.Bucket + "/" + e.Object 538 } 539 540 // Error returns string an error formatted as the given text. 541 func (e ObjectNamePrefixAsSlash) Error() string { 542 return "Object name contains forward slash as prefix: " + e.Bucket + "/" + e.Object 543 } 544 545 // AllAccessDisabled All access to this object has been disabled 546 type AllAccessDisabled GenericError 547 548 // Error returns string an error formatted as the given text. 549 func (e AllAccessDisabled) Error() string { 550 return "All access to this object has been disabled" 551 } 552 553 // IncompleteBody You did not provide the number of bytes specified by the Content-Length HTTP header. 554 type IncompleteBody GenericError 555 556 // Error returns string an error formatted as the given text. 557 func (e IncompleteBody) Error() string { 558 return e.Bucket + "/" + e.Object + " has incomplete body" 559 } 560 561 // InvalidRange - invalid range typed error. 562 type InvalidRange struct { 563 OffsetBegin int64 564 OffsetEnd int64 565 ResourceSize int64 566 } 567 568 func (e InvalidRange) Error() string { 569 return fmt.Sprintf("The requested range \"bytes %d -> %d of %d\" is not satisfiable.", e.OffsetBegin, e.OffsetEnd, e.ResourceSize) 570 } 571 572 // ObjectTooLarge error returned when the size of the object > max object size allowed (5G) per request. 573 type ObjectTooLarge GenericError 574 575 func (e ObjectTooLarge) Error() string { 576 return "size of the object greater than what is allowed(5G)" 577 } 578 579 // ObjectTooSmall error returned when the size of the object < what is expected. 580 type ObjectTooSmall GenericError 581 582 func (e ObjectTooSmall) Error() string { 583 return "size of the object less than what is expected" 584 } 585 586 // OperationTimedOut - a timeout occurred. 587 type OperationTimedOut struct{} 588 589 func (e OperationTimedOut) Error() string { 590 return "Operation timed out" 591 } 592 593 // Multipart related errors. 594 595 // MalformedUploadID malformed upload id. 596 type MalformedUploadID struct { 597 UploadID string 598 } 599 600 func (e MalformedUploadID) Error() string { 601 return "Malformed upload id " + e.UploadID 602 } 603 604 // InvalidUploadID invalid upload id. 605 type InvalidUploadID struct { 606 Bucket string 607 Object string 608 UploadID string 609 } 610 611 func (e InvalidUploadID) Error() string { 612 return "Invalid upload id " + e.UploadID 613 } 614 615 // InvalidPart One or more of the specified parts could not be found 616 type InvalidPart struct { 617 PartNumber int 618 ExpETag string 619 GotETag string 620 } 621 622 func (e InvalidPart) Error() string { 623 return fmt.Sprintf("Specified part could not be found. PartNumber %d, Expected %s, got %s", 624 e.PartNumber, e.ExpETag, e.GotETag) 625 } 626 627 // PartTooSmall - error if part size is less than 5MB. 628 type PartTooSmall struct { 629 PartSize int64 630 PartNumber int 631 PartETag string 632 } 633 634 func (e PartTooSmall) Error() string { 635 return fmt.Sprintf("Part size for %d should be at least 5MB", e.PartNumber) 636 } 637 638 // PartTooBig returned if size of part is bigger than the allowed limit. 639 type PartTooBig struct{} 640 641 func (e PartTooBig) Error() string { 642 return "Part size bigger than the allowed limit" 643 } 644 645 // InvalidETag error returned when the etag has changed on disk 646 type InvalidETag struct{} 647 648 func (e InvalidETag) Error() string { 649 return "etag of the object has changed" 650 } 651 652 // BackendDown is returned for network errors 653 type BackendDown struct { 654 Err string 655 } 656 657 func (e BackendDown) Error() string { 658 return e.Err 659 } 660 661 // NotImplemented If a feature is not implemented 662 type NotImplemented struct { 663 Message string 664 } 665 666 func (e NotImplemented) Error() string { 667 return e.Message 668 } 669 670 // UnsupportedMetadata - unsupported metadata 671 type UnsupportedMetadata struct{} 672 673 func (e UnsupportedMetadata) Error() string { 674 return "Unsupported headers in Metadata" 675 } 676 677 // isErrBucketNotFound - Check if error type is BucketNotFound. 678 func isErrBucketNotFound(err error) bool { 679 var bkNotFound BucketNotFound 680 return errors.As(err, &bkNotFound) 681 } 682 683 // isErrReadQuorum check if the error type is InsufficientReadQuorum 684 func isErrReadQuorum(err error) bool { 685 var rquorum InsufficientReadQuorum 686 return errors.As(err, &rquorum) 687 } 688 689 // isErrWriteQuorum check if the error type is InsufficientWriteQuorum 690 func isErrWriteQuorum(err error) bool { 691 var rquorum InsufficientWriteQuorum 692 return errors.As(err, &rquorum) 693 } 694 695 // isErrObjectNotFound - Check if error type is ObjectNotFound. 696 func isErrObjectNotFound(err error) bool { 697 var objNotFound ObjectNotFound 698 return errors.As(err, &objNotFound) 699 } 700 701 // isErrVersionNotFound - Check if error type is VersionNotFound. 702 func isErrVersionNotFound(err error) bool { 703 var versionNotFound VersionNotFound 704 return errors.As(err, &versionNotFound) 705 } 706 707 // isErrSignatureDoesNotMatch - Check if error type is SignatureDoesNotMatch. 708 func isErrSignatureDoesNotMatch(err error) bool { 709 var signatureDoesNotMatch SignatureDoesNotMatch 710 return errors.As(err, &signatureDoesNotMatch) 711 } 712 713 // PreConditionFailed - Check if copy precondition failed 714 type PreConditionFailed struct{} 715 716 func (e PreConditionFailed) Error() string { 717 return "At least one of the pre-conditions you specified did not hold" 718 } 719 720 func isErrPreconditionFailed(err error) bool { 721 _, ok := err.(PreConditionFailed) 722 return ok 723 } 724 725 // isErrMethodNotAllowed - Check if error type is MethodNotAllowed. 726 func isErrMethodNotAllowed(err error) bool { 727 var methodNotAllowed MethodNotAllowed 728 return errors.As(err, &methodNotAllowed) 729 } 730 731 func isErrInvalidRange(err error) bool { 732 _, ok := err.(InvalidRange) 733 return ok 734 } 735 736 // ReplicationPermissionCheck - Check if error type is ReplicationPermissionCheck. 737 type ReplicationPermissionCheck struct{} 738 739 func (e ReplicationPermissionCheck) Error() string { 740 return "Replication permission validation requests cannot be completed" 741 } 742 743 func isReplicationPermissionCheck(err error) bool { 744 _, ok := err.(ReplicationPermissionCheck) 745 return ok 746 }