Don’t retry errors by default

This commit is contained in:
Max Radermacher 2026-01-26 12:47:51 -06:00 committed by GitHub
parent 63809bccf6
commit 9d20237b9c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 17 additions and 152 deletions

View File

@ -65,9 +65,7 @@ public protocol TSAccountManager {
func lastSetIsDiscoverableByPhoneNumber(tx: DBReadTransaction) -> Date
}
public struct NotRegisteredError: Error, IsRetryableProvider {
public let isRetryableProvider: Bool = false
}
public struct NotRegisteredError: Error {}
/// It's *possible* (but implausible) that the local user's "device ID"
/// isn't valid. These "device IDs" aren't supported on the server, so these

View File

@ -6,7 +6,7 @@
import Foundation
public import LibSignalClient
public enum MessageSenderError: Error, IsRetryableProvider, UserErrorDescriptionProvider {
public enum MessageSenderError: Error, UserErrorDescriptionProvider {
case blockedContactRecipient
case threadMissing
@ -24,17 +24,6 @@ public enum MessageSenderError: Error, IsRetryableProvider, UserErrorDescription
)
}
}
// MARK: - IsRetryableProvider
public var isRetryableProvider: Bool {
switch self {
case .blockedContactRecipient:
return false
case .threadMissing:
return false
}
}
}
// MARK: -
@ -57,7 +46,7 @@ extension Error {
// MARK: -
public class MessageSenderNoSuchSignalRecipientError: CustomNSError, IsRetryableProvider, UserErrorDescriptionProvider {
public class MessageSenderNoSuchSignalRecipientError: CustomNSError, UserErrorDescriptionProvider {
// NSError bridging: the domain of the error.
public static let errorDomain = OWSError.errorDomain
@ -81,16 +70,11 @@ public class MessageSenderNoSuchSignalRecipientError: CustomNSError, IsRetryable
}
public init() {}
// MARK: - IsRetryableProvider
// No need to retry if the recipient is not registered.
public var isRetryableProvider: Bool { false }
}
// MARK: -
public class MessageSenderErrorNoValidRecipients: CustomNSError, IsRetryableProvider, UserErrorDescriptionProvider {
public class MessageSenderErrorNoValidRecipients: CustomNSError, UserErrorDescriptionProvider {
static var asNSError: NSError {
MessageSenderErrorNoValidRecipients() as Error as NSError
}
@ -112,21 +96,17 @@ public class MessageSenderErrorNoValidRecipients: CustomNSError, IsRetryableProv
comment: "Error indicating that an outgoing message had no valid recipients.",
)
}
public var isRetryableProvider: Bool { false }
}
// MARK: -
class MessageSenderNoSessionForTransientMessageError: CustomNSError, IsRetryableProvider, UserErrorDescriptionProvider {
class MessageSenderNoSessionForTransientMessageError: CustomNSError, UserErrorDescriptionProvider {
// NSError bridging: the domain of the error.
static let errorDomain = OWSError.errorDomain
// NSError bridging: the error code within the given domain.
var errorCode: Int { OWSErrorCode.noSessionForTransientMessage.rawValue }
var isRetryableProvider: Bool { false }
var localizedDescription: String {
// These messages are never presented to the user, since these errors only
// occur to transient messages. We only specify an error to avoid an assert.
@ -136,7 +116,7 @@ class MessageSenderNoSessionForTransientMessageError: CustomNSError, IsRetryable
// MARK: -
public class UntrustedIdentityError: CustomNSError, IsRetryableProvider, UserErrorDescriptionProvider {
public class UntrustedIdentityError: CustomNSError, UserErrorDescriptionProvider {
public let serviceId: ServiceId
init(serviceId: ServiceId) {
@ -165,10 +145,6 @@ public class UntrustedIdentityError: CustomNSError, IsRetryableProvider, UserErr
// NSError bridging: the error code within the given domain.
public var errorCode: Int { Self.errorCode }
/// Key will continue to be unaccepted, so no need to retry. It'll only
/// cause us to hit the Pre-Key request rate limit.
public var isRetryableProvider: Bool { false }
}
public class InvalidKeySignatureError: CustomNSError, IsRetryableProvider, UserErrorDescriptionProvider {
@ -212,7 +188,7 @@ public class InvalidKeySignatureError: CustomNSError, IsRetryableProvider, UserE
// MARK: -
public class SpamChallengeRequiredError: CustomNSError, IsRetryableProvider, UserErrorDescriptionProvider {
public class SpamChallengeRequiredError: CustomNSError, UserErrorDescriptionProvider {
// NSError bridging: the domain of the error.
public static let errorDomain = OWSError.errorDomain
@ -230,8 +206,6 @@ public class SpamChallengeRequiredError: CustomNSError, IsRetryableProvider, Use
// NSError bridging: the error code within the given domain.
public var errorCode: Int { OWSErrorCode.serverRejectedSuspectedSpam.rawValue }
public var isRetryableProvider: Bool { false }
}
// MARK: -
@ -272,7 +246,7 @@ class OWSRetryableMessageSenderError: Error, IsRetryableProvider {
// MARK: -
class MessageDeletedBeforeSentError: CustomNSError, IsRetryableProvider {
class MessageDeletedBeforeSentError: CustomNSError {
static var asNSError: NSError {
MessageDeletedBeforeSentError() as Error as NSError
}
@ -282,6 +256,4 @@ class MessageDeletedBeforeSentError: CustomNSError, IsRetryableProvider {
// NSError bridging: the error code within the given domain.
var errorCode: Int { OWSErrorCode.messageDeletedBeforeSent.rawValue }
var isRetryableProvider: Bool { false }
}

View File

@ -6,10 +6,8 @@
import CryptoKit
public import LibSignalClient
public enum IdentityManagerError: Error, IsRetryableProvider {
public enum IdentityManagerError: Error {
case identityKeyMismatchForOutgoingMessage
public var isRetryableProvider: Bool { false }
}
public protocol OWSIdentityManager {

View File

@ -6,12 +6,8 @@
import Foundation
@objc
public enum StickerError: Int, Error, IsRetryableProvider {
public enum StickerError: Int, Error {
case invalidInput
case noSticker
case corruptData
// MARK: - IsRetryableProvider
public var isRetryableProvider: Bool { false }
}

View File

@ -13,17 +13,6 @@ public enum OWSUDError: Error {
// MARK: -
extension OWSUDError: IsRetryableProvider {
public var isRetryableProvider: Bool {
switch self {
case .assertionError, .invalidData:
return false
}
}
}
// MARK: -
public enum UnidentifiedAccessMode: Int {
case unknown
case enabled

View File

@ -5,14 +5,8 @@
import Foundation
public enum OWSURLSessionError: Error, IsRetryableProvider {
public enum OWSURLSessionError: Error {
case responseTooLarge
public var isRetryableProvider: Bool {
switch self {
case .responseTooLarge: return false
}
}
}
public class OWSURLSession: OWSURLSessionProtocol {

View File

@ -47,16 +47,9 @@ extension ProfileFetcher {
}
}
public enum ProfileFetcherError: Error, IsRetryableProvider {
public enum ProfileFetcherError: Error {
case skippingOpportunisticFetch
case couldNotFetchCredential
public var isRetryableProvider: Bool {
switch self {
case .skippingOpportunisticFetch: false
case .couldNotFetchCredential: false
}
}
}
public actor ProfileFetcherImpl: ProfileFetcher {

View File

@ -4,11 +4,7 @@
//
extension Stripe {
public struct StripeError: Error, IsRetryableProvider {
public struct StripeError: Error {
public let code: String
public var isRetryableProvider: Bool {
return false
}
}
}

View File

@ -79,7 +79,7 @@ public enum Upload {
case uploaded(Int)
}
public enum Error: Swift.Error, IsRetryableProvider, LocalizedError, Equatable {
public enum Error: Swift.Error, LocalizedError, Equatable {
case invalidUploadURL
case networkError
case networkTimeout
@ -90,13 +90,6 @@ public enum Upload {
case missingFile
case unknown
public var isRetryableProvider: Bool {
switch self {
case .invalidUploadURL, .uploadFailure, .partialUpload, .unsupportedEndpoint, .unexpectedResponseStatusCode, .networkTimeout, .networkError, .missingFile, .unknown:
return false
}
}
public var errorDescription: String? {
localizedDescription
}

View File

@ -6,9 +6,7 @@
import Foundation
/// Thrown when a request isn't sent because the app is already expired.
public struct AppExpiredError: Error, IsRetryableProvider {
public let isRetryableProvider: Bool = false
}
public struct AppExpiredError: Error {}
public final class AppExpiry {

View File

@ -5,19 +5,8 @@
import Foundation
import ObjectiveC
public import SwiftProtobuf
extension Error {
public var hasIsRetryable: Bool {
if self is IsRetryableProvider {
return true
}
if self.isNetworkFailureOrTimeout {
return true
}
return false
}
public var isRetryable: Bool {
// Error and NSError have a special relationship.
// They can be "cast" back and forth, but are separate objects.
@ -46,14 +35,7 @@ extension Error {
return true
}
// This value should always be set for all errors by this
// var is consulted. If not, default to retrying in production.
if CurrentAppContext().isRunningTests {
Logger.warn("Error without retry behavior specified: \(self)")
} else {
owsFailDebug("Error without retry behavior specified: \(self)")
}
return true
return false
}
}
@ -65,28 +47,6 @@ public protocol IsRetryableProvider {
// MARK: -
extension OWSAssertionError: IsRetryableProvider {
public var isRetryableProvider: Bool { false }
}
extension OWSGenericError: IsRetryableProvider {
public var isRetryableProvider: Bool { false }
}
extension CancellationError: IsRetryableProvider {
public var isRetryableProvider: Bool { false }
}
extension SwiftProtobuf.BinaryDecodingError: IsRetryableProvider {
public var isRetryableProvider: Bool { false }
}
extension SwiftProtobuf.BinaryEncodingError: IsRetryableProvider {
public var isRetryableProvider: Bool { false }
}
// MARK: -
// NOTE: We typically prefer to use a more specific error.
public class OWSRetryableError: CustomNSError, IsRetryableProvider {
public static var asNSError: NSError {

View File

@ -22,27 +22,21 @@ class OWSErrorTest: XCTestCase {
let error4 = OWSRetryableError()
let error5 = MessageSenderNoSuchSignalRecipientError()
XCTAssertFalse(errorFooBar.hasIsRetryable)
XCTAssertTrue(errorFooBar.isRetryable)
XCTAssertFalse(errorFooBar.isRetryable)
XCTAssertFalse(errorFooBar.isFatalError)
XCTAssertTrue(errorGeneric.hasIsRetryable)
XCTAssertFalse(errorGeneric.isRetryable)
XCTAssertFalse(errorGeneric.isFatalError)
XCTAssertTrue(error1.hasIsRetryable)
XCTAssertFalse(error1.isRetryable)
XCTAssertFalse(error1.isFatalError)
XCTAssertTrue(error2.hasIsRetryable)
XCTAssertTrue(error2.isRetryable)
XCTAssertFalse(error2.isFatalError)
XCTAssertTrue(error4.hasIsRetryable)
XCTAssertTrue(error4.isRetryable)
XCTAssertFalse(error4.isFatalError)
XCTAssertTrue(error5.hasIsRetryable)
XCTAssertFalse(error5.isRetryable)
XCTAssertFalse(error5.isFatalError)
}
@ -57,7 +51,6 @@ class OWSErrorTest: XCTestCase {
XCTAssertEqual((error1 as NSError).domain, OWSError.errorDomain)
XCTAssertTrue(error1.hasUserErrorDescription)
XCTAssertEqual(error1.userErrorDescription, errorDescription1)
XCTAssertTrue(error1.hasIsRetryable)
XCTAssertEqual(error1.isRetryable, isRetryable1)
let nsError1: NSError = error1 as NSError
@ -65,7 +58,6 @@ class OWSErrorTest: XCTestCase {
XCTAssertEqual(nsError1.domain, OWSError.errorDomain)
XCTAssertTrue(nsError1.hasUserErrorDescription)
XCTAssertEqual(nsError1.userErrorDescription, errorDescription1)
XCTAssertTrue(nsError1.hasIsRetryable)
XCTAssertEqual(nsError1.isRetryable, isRetryable1)
do {
@ -76,7 +68,6 @@ class OWSErrorTest: XCTestCase {
XCTAssertEqual((error as NSError).domain, OWSError.errorDomain)
XCTAssertTrue(error.hasUserErrorDescription)
XCTAssertEqual(error.userErrorDescription, errorDescription1)
XCTAssertTrue(error.hasIsRetryable)
XCTAssertEqual(error.isRetryable, isRetryable1)
}
@ -88,7 +79,6 @@ class OWSErrorTest: XCTestCase {
XCTAssertEqual((error as NSError).domain, OWSError.errorDomain)
XCTAssertTrue(error.hasUserErrorDescription)
XCTAssertEqual(error.userErrorDescription, errorDescription1)
XCTAssertTrue(error.hasIsRetryable)
XCTAssertEqual(error.isRetryable, isRetryable1)
}
}
@ -103,7 +93,6 @@ class OWSErrorTest: XCTestCase {
XCTAssertEqual((error1 as NSError).domain, OWSError.errorDomain)
XCTAssertTrue(error1.hasUserErrorDescription)
XCTAssertEqual(error1.userErrorDescription, errorDescription1)
XCTAssertTrue(error1.hasIsRetryable)
XCTAssertEqual(error1.isRetryable, isRetryable1)
let nsError1: NSError = error1 as NSError
@ -111,7 +100,6 @@ class OWSErrorTest: XCTestCase {
XCTAssertEqual(nsError1.domain, OWSError.errorDomain)
XCTAssertTrue(nsError1.hasUserErrorDescription)
XCTAssertEqual(nsError1.userErrorDescription, errorDescription1)
XCTAssertTrue(nsError1.hasIsRetryable)
XCTAssertEqual(nsError1.isRetryable, isRetryable1)
do {
@ -122,7 +110,6 @@ class OWSErrorTest: XCTestCase {
XCTAssertEqual((error as NSError).domain, OWSError.errorDomain)
XCTAssertTrue(error.hasUserErrorDescription)
XCTAssertEqual(error.userErrorDescription, errorDescription1)
XCTAssertTrue(error.hasIsRetryable)
XCTAssertEqual(error.isRetryable, isRetryable1)
}
@ -134,7 +121,6 @@ class OWSErrorTest: XCTestCase {
XCTAssertEqual((error as NSError).domain, OWSError.errorDomain)
XCTAssertTrue(error.hasUserErrorDescription)
XCTAssertEqual(error.userErrorDescription, errorDescription1)
XCTAssertTrue(error.hasIsRetryable)
XCTAssertEqual(error.isRetryable, isRetryable1)
}
}
@ -153,7 +139,6 @@ class OWSErrorTest: XCTestCase {
XCTAssertEqual((error1 as NSError).domain, OWSError.errorDomain)
XCTAssertTrue(error1.hasUserErrorDescription)
XCTAssertEqual(error1.userErrorDescription, errorDescription1)
XCTAssertTrue(error1.hasIsRetryable)
XCTAssertEqual(error1.isRetryable, isRetryable1)
let nsError1: NSError = error1 as NSError
@ -161,7 +146,6 @@ class OWSErrorTest: XCTestCase {
XCTAssertEqual(nsError1.domain, OWSError.errorDomain)
XCTAssertTrue(nsError1.hasUserErrorDescription)
XCTAssertEqual(nsError1.userErrorDescription, errorDescription1)
XCTAssertTrue(nsError1.hasIsRetryable)
XCTAssertEqual(nsError1.isRetryable, isRetryable1)
do {
@ -172,7 +156,6 @@ class OWSErrorTest: XCTestCase {
XCTAssertEqual((error as NSError).domain, OWSError.errorDomain)
XCTAssertTrue(error.hasUserErrorDescription)
XCTAssertEqual(error.userErrorDescription, errorDescription1)
XCTAssertTrue(error.hasIsRetryable)
XCTAssertEqual(error.isRetryable, isRetryable1)
}
@ -184,7 +167,6 @@ class OWSErrorTest: XCTestCase {
XCTAssertEqual((error as NSError).domain, OWSError.errorDomain)
XCTAssertTrue(error.hasUserErrorDescription)
XCTAssertEqual(error.userErrorDescription, errorDescription1)
XCTAssertTrue(error.hasIsRetryable)
XCTAssertEqual(error.isRetryable, isRetryable1)
}
}
@ -204,12 +186,10 @@ class OWSErrorTest: XCTestCase {
XCTAssertEqual((error1 as NSError).code, errorCode1)
XCTAssertEqual((error1 as NSError).domain, OWSError.errorDomain)
XCTAssertFalse(error1.hasUserErrorDescription)
XCTAssertFalse(error1.hasIsRetryable)
XCTAssertEqual(nsError1.code, errorCode1)
XCTAssertEqual(nsError1.domain, OWSError.errorDomain)
XCTAssertFalse(nsError1.hasUserErrorDescription)
XCTAssertFalse(nsError1.hasIsRetryable)
do {
try ErrorThrower(error: error1).performThrow()
@ -218,7 +198,6 @@ class OWSErrorTest: XCTestCase {
XCTAssertEqual((error as NSError).code, errorCode1)
XCTAssertEqual((error as NSError).domain, OWSError.errorDomain)
XCTAssertFalse(error.hasUserErrorDescription)
XCTAssertFalse(error.hasIsRetryable)
}
do {
@ -228,7 +207,6 @@ class OWSErrorTest: XCTestCase {
XCTAssertEqual((error as NSError).code, errorCode1)
XCTAssertEqual((error as NSError).domain, OWSError.errorDomain)
XCTAssertFalse(error.hasUserErrorDescription)
XCTAssertFalse(error.hasIsRetryable)
}
}