diff --git a/Signal/src/environment/Migrations/OWS103EnableVideoCalling.m b/Signal/src/environment/Migrations/OWS103EnableVideoCalling.m
index b3f2e71bf..678ac2055 100644
--- a/Signal/src/environment/Migrations/OWS103EnableVideoCalling.m
+++ b/Signal/src/environment/Migrations/OWS103EnableVideoCalling.m
@@ -34,6 +34,10 @@ static NSString *const OWS103EnableVideoCallingMigrationId = @"103";
                                                        [self save];
                                                    }
                                                    failure:^(NSURLSessionDataTask *task, NSError *error) {
+                                                       if (!IsNSErrorNetworkFailure(error)) {
+                                                           OWSProdErrorWNSError(
+                                                               @"error_enable_video_calling_request_failed", error);
+                                                       }
                                                        DDLogError(@"%@ failed with error: %@", self.tag, error);
                                                    }];
                                            }];
diff --git a/Signal/src/environment/VersionMigrations.m b/Signal/src/environment/VersionMigrations.m
index d72a4333e..4ba182cbf 100644
--- a/Signal/src/environment/VersionMigrations.m
+++ b/Signal/src/environment/VersionMigrations.m
@@ -115,23 +115,7 @@
     return [thisVersionString compare:thatVersionString options:NSNumericSearch] == NSOrderedAscending;
 }
 
-#pragma mark Upgrading to 2.1 - Needs to register VOIP token + Removing video cache folder
-
-+ (void)nonBlockingPushRegistration {
-    void (^failedBlock)(NSError *) = ^(NSError *error) {
-        DDLogError(@"Failed to register VOIP push token: %@", error.debugDescription);
-    };
-    [[PushManager sharedManager] requestPushTokenWithSuccess:^(NSString *pushToken, NSString *voipToken) {
-        [[TSAccountManager sharedInstance]
-            registerForPushNotificationsWithPushToken:pushToken
-                                            voipToken:voipToken
-                                              success:^{
-                                                  DDLogWarn(@"Registered for VOIP Push.");
-                                              }
-                                              failure:failedBlock];
-    }
-                                                     failure:failedBlock];
-}
+#pragma mark Upgrading to 2.1 - Removing video cache folder
 
 + (void)clearVideoCache {
     NSArray *paths     = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
@@ -162,13 +146,16 @@
       TSUpdateAttributesRequest *request = [[TSUpdateAttributesRequest alloc] initWithUpdatedAttributesWithVoice];
       [[TSNetworkManager sharedManager] makeRequest:request
           success:^(NSURLSessionDataTask *task, id responseObject) {
-            success = YES;
-            dispatch_semaphore_signal(sema);
+              success = YES;
+              dispatch_semaphore_signal(sema);
           }
           failure:^(NSURLSessionDataTask *task, NSError *error) {
-            success = NO;
-            DDLogError(@"Updating attributess failed with error: %@", error.description);
-            dispatch_semaphore_signal(sema);
+              if (!IsNSErrorNetworkFailure(error)) {
+                  OWSProdErrorWNSError(@"error_update_attributes_request_failed", error);
+              }
+              success = NO;
+              DDLogError(@"Updating attributess failed with error: %@", error.description);
+              dispatch_semaphore_signal(sema);
           }];
 
 
diff --git a/SignalServiceKit/src/Account/TSAccountManager.m b/SignalServiceKit/src/Account/TSAccountManager.m
index 284a615eb..6ce48ff9a 100644
--- a/SignalServiceKit/src/Account/TSAccountManager.m
+++ b/SignalServiceKit/src/Account/TSAccountManager.m
@@ -159,6 +159,9 @@ NSString *const kNSNotificationName_LocalNumberDidChange = @"kNSNotificationName
                                                         failure:failureHandler
                                                remainingRetries:remainingRetries - 1];
             } else {
+                if (!IsNSErrorNetworkFailure(error)) {
+                    OWSProdErrorWNSError(@"accounts_error_register_push_tokens_failed", error);
+                }
                 failureHandler(error);
             }
         }];
@@ -180,7 +183,7 @@ NSString *const kNSNotificationName_LocalNumberDidChange = @"kNSNotificationName
     // we make our verification code request.
     TSAccountManager *manager = [self sharedInstance];
     manager.phoneNumberAwaitingVerification = phoneNumber;
-    
+
     [[TSNetworkManager sharedManager]
         makeRequest:[[TSRequestVerificationCodeRequest alloc]
                         initWithPhoneNumber:phoneNumber
@@ -193,6 +196,9 @@ NSString *const kNSNotificationName_LocalNumberDidChange = @"kNSNotificationName
             successBlock();
         }
         failure:^(NSURLSessionDataTask *task, NSError *error) {
+            if (!IsNSErrorNetworkFailure(error)) {
+                OWSProdErrorWNSError(@"accounts_error_verification_code_request_failed", error);
+            }
             DDLogError(@"%@ Failed to request verification code request with error:%@", self.tag, error);
             failureBlock(error);
         }];
@@ -263,6 +269,9 @@ NSString *const kNSNotificationName_LocalNumberDidChange = @"kNSNotificationName
             }
         }
         failure:^(NSURLSessionDataTask *task, NSError *error) {
+            if (!IsNSErrorNetworkFailure(error)) {
+                OWSProdErrorWNSError(@"accounts_error_verify_account_request_failed", error);
+            }
             DDLogWarn(@"%@ Error verifying code: %@", self.tag, error.debugDescription);
             switch (error.code) {
                 case 403: {
@@ -316,6 +325,9 @@ NSString *const kNSNotificationName_LocalNumberDidChange = @"kNSNotificationName
                                                               userInfo:nil];
         }
         failure:^(NSURLSessionDataTask *task, NSError *error) {
+            if (!IsNSErrorNetworkFailure(error)) {
+                OWSProdErrorWNSError(@"accounts_error_unregister_account_request_failed", error);
+            }
             DDLogError(@"%@ Failed to unregister with error: %@", self.tag, error);
             failureBlock(error);
         }];
diff --git a/SignalServiceKit/src/Account/TSPreKeyManager.m b/SignalServiceKit/src/Account/TSPreKeyManager.m
index a7b8a1102..4e9097f6d 100644
--- a/SignalServiceKit/src/Account/TSPreKeyManager.m
+++ b/SignalServiceKit/src/Account/TSPreKeyManager.m
@@ -181,10 +181,12 @@ static const NSTimeInterval kSignedPreKeyUpdateFailureMaxFailureDuration = 10 *
                 [TSPreKeyManager clearPreKeyUpdateFailureCount];
             }
             failure:^(NSURLSessionDataTask *task, NSError *error) {
-                if (modeCopy == RefreshPreKeysMode_SignedAndOneTime) {
-                    OWSProdErrorWNSError(@"error_prekeys_update_failed_signed_and_onetime", error);
-                } else {
-                    OWSProdErrorWNSError(@"error_prekeys_update_failed_just_signed", error);
+                if (!IsNSErrorNetworkFailure(error)) {
+                    if (modeCopy == RefreshPreKeysMode_SignedAndOneTime) {
+                        OWSProdErrorWNSError(@"error_prekeys_update_failed_signed_and_onetime", error);
+                    } else {
+                        OWSProdErrorWNSError(@"error_prekeys_update_failed_just_signed", error);
+                    }
                 }
 
                 // Mark the prekeys as _NOT_ checked on failure.
@@ -302,6 +304,9 @@ static const NSTimeInterval kSignedPreKeyUpdateFailureMaxFailureDuration = 10 *
                         }
                     }
                     failure:^(NSURLSessionDataTask *task, NSError *error) {
+                        if (!IsNSErrorNetworkFailure(error)) {
+                            OWSProdErrorWNSError(@"error_prekeys_current_signed_prekey_request_failed", error);
+                        }
                         DDLogWarn(@"%@ Could not retrieve current signed key from the service.", self.tag);
 
                         // Mark the prekeys as _NOT_ checked on failure.
@@ -310,6 +315,9 @@ static const NSTimeInterval kSignedPreKeyUpdateFailureMaxFailureDuration = 10 *
             }
         }
         failure:^(NSURLSessionDataTask *task, NSError *error) {
+            if (!IsNSErrorNetworkFailure(error)) {
+                OWSProdErrorWNSError(@"error_prekeys_available_prekeys_request_failed", error);
+            }
             DDLogError(@"%@ Failed to retrieve the number of available prekeys.", self.tag);
 
             // Mark the prekeys as _NOT_ checked on failure.
diff --git a/SignalServiceKit/src/Contacts/ContactsUpdater.m b/SignalServiceKit/src/Contacts/ContactsUpdater.m
index cf1be99e5..5026c19e5 100644
--- a/SignalServiceKit/src/Contacts/ContactsUpdater.m
+++ b/SignalServiceKit/src/Contacts/ContactsUpdater.m
@@ -166,45 +166,49 @@ NS_ASSUME_NONNULL_BEGIN
       TSRequest *request = [[TSContactsIntersectionRequest alloc] initWithHashesArray:hashes];
       [[TSNetworkManager sharedManager] makeRequest:request
           success:^(NSURLSessionDataTask *tsTask, id responseDict) {
-            NSMutableDictionary *attributesForIdentifier = [NSMutableDictionary dictionary];
-            NSArray *contactsArray                       = [(NSDictionary *)responseDict objectForKey:@"contacts"];
-
-            // Map attributes to phone numbers
-            if (contactsArray) {
-                for (NSDictionary *dict in contactsArray) {
-                    NSString *hash       = [dict objectForKey:@"token"];
-                    NSString *identifier = [phoneNumbersByHashes objectForKey:hash];
-
-                    if (!identifier) {
-                        DDLogWarn(@"%@ An interesecting hash wasn't found in the mapping.", self.tag);
-                        break;
-                    }
-
-                    [attributesForIdentifier setObject:dict forKey:identifier];
-                }
-            }
-
-            // Insert or update contact attributes
-            [[TSStorageManager sharedManager].dbReadWriteConnection
-                readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
-                    for (NSString *identifier in attributesForIdentifier) {
-                        SignalRecipient *recipient =
-                            [SignalRecipient recipientWithTextSecureIdentifier:identifier withTransaction:transaction];
-                        if (!recipient) {
-                            recipient = [[SignalRecipient alloc] initWithTextSecureIdentifier:identifier relay:nil];
-                        }
-
-                        NSDictionary *attributes = [attributesForIdentifier objectForKey:identifier];
-
-                        recipient.relay = attributes[@"relay"];
-
-                        [recipient saveWithTransaction:transaction];
-                    }
-                }];
-
-            success([NSSet setWithArray:attributesForIdentifier.allKeys]);
+              NSMutableDictionary *attributesForIdentifier = [NSMutableDictionary dictionary];
+              NSArray *contactsArray = [(NSDictionary *)responseDict objectForKey:@"contacts"];
+
+              // Map attributes to phone numbers
+              if (contactsArray) {
+                  for (NSDictionary *dict in contactsArray) {
+                      NSString *hash = [dict objectForKey:@"token"];
+                      NSString *identifier = [phoneNumbersByHashes objectForKey:hash];
+
+                      if (!identifier) {
+                          DDLogWarn(@"%@ An interesecting hash wasn't found in the mapping.", self.tag);
+                          break;
+                      }
+
+                      [attributesForIdentifier setObject:dict forKey:identifier];
+                  }
+              }
+
+              // Insert or update contact attributes
+              [[TSStorageManager sharedManager].dbReadWriteConnection
+                  readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
+                      for (NSString *identifier in attributesForIdentifier) {
+                          SignalRecipient *recipient = [SignalRecipient recipientWithTextSecureIdentifier:identifier
+                                                                                          withTransaction:transaction];
+                          if (!recipient) {
+                              recipient = [[SignalRecipient alloc] initWithTextSecureIdentifier:identifier relay:nil];
+                          }
+
+                          NSDictionary *attributes = [attributesForIdentifier objectForKey:identifier];
+
+                          recipient.relay = attributes[@"relay"];
+
+                          [recipient saveWithTransaction:transaction];
+                      }
+                  }];
+
+              success([NSSet setWithArray:attributesForIdentifier.allKeys]);
           }
           failure:^(NSURLSessionDataTask *task, NSError *error) {
+              if (!IsNSErrorNetworkFailure(error)) {
+                  OWSProdErrorWNSError(@"contacts_error_contacts_intersection_failed", error);
+              }
+
               NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
               if (response.statusCode == 413) {
                   failure(OWSErrorWithCodeDescription(
diff --git a/SignalServiceKit/src/Messages/Attachments/OWSAttachmentsProcessor.m b/SignalServiceKit/src/Messages/Attachments/OWSAttachmentsProcessor.m
index fabf2bc62..2f0be843d 100644
--- a/SignalServiceKit/src/Messages/Attachments/OWSAttachmentsProcessor.m
+++ b/SignalServiceKit/src/Messages/Attachments/OWSAttachmentsProcessor.m
@@ -143,67 +143,68 @@ static const CGFloat kAttachmentDownloadProgressTheta = 0.001f;
     TSAttachmentRequest *attachmentRequest = [[TSAttachmentRequest alloc] initWithId:attachment.serverId relay:attachment.relay];
 
     [self.networkManager makeRequest:attachmentRequest
-                             success:^(NSURLSessionDataTask *task, id responseObject) {
-                                 if (![responseObject isKindOfClass:[NSDictionary class]]) {
-                                     DDLogError(@"%@ Failed retrieval of attachment. Response had unexpected format.",
-                                         self.tag);
-                                     NSError *error = OWSErrorMakeUnableToProcessServerResponseError();
-                                     return markAndHandleFailure(error);
-                                 }
-                                 NSString *location = [(NSDictionary *)responseObject objectForKey:@"location"];
-                                 if (!location) {
-                                     DDLogError(
-                                         @"%@ Failed retrieval of attachment. Response had no location.", self.tag);
-                                     NSError *error = OWSErrorMakeUnableToProcessServerResponseError();
-                                     return markAndHandleFailure(error);
-                                 }
-
-                                 dispatch_async([OWSDispatch attachmentsQueue], ^{
-                                     [self downloadFromLocation:location
-                                         pointer:attachment
-                                         success:^(NSData *_Nonnull encryptedData) {
-                                             [self decryptAttachmentData:encryptedData
-                                                                 pointer:attachment
-                                                                 success:markAndHandleSuccess
-                                                                 failure:markAndHandleFailure];
-                                         }
-                                         failure:^(NSURLSessionDataTask *_Nullable task, NSError *_Nonnull error) {
-                                             if (attachment.serverId < 100) {
-                                                 // This looks like the symptom of the "frequent 404
-                                                 // downloading attachments with low server ids".
-                                                 NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
-                                                 NSInteger statusCode = [httpResponse statusCode];
-                                                 DDLogError(@"%@ %d Failure with suspicious attachment id: %llu, %@",
-                                                     self.tag,
-                                                     (int)statusCode,
-                                                     (unsigned long long)attachment.serverId,
-                                                     error);
-                                                 [DDLog flushLog];
-                                                 OWSAssert(0);
-                                             }
-                                             if (markAndHandleFailure) {
-                                                 markAndHandleFailure(error);
-                                             }
-                                         }];
-                                 });
-                             }
-                             failure:^(NSURLSessionDataTask *task, NSError *error) {
-                                 DDLogError(@"Failed retrieval of attachment with error: %@", error);
-                                 if (attachment.serverId < 100) {
-                                     // This _shouldn't_ be the symptom of the "frequent 404
-                                     // downloading attachments with low server ids".
-                                     NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
-                                     NSInteger statusCode = [httpResponse statusCode];
-                                     DDLogError(@"%@ %d Failure with suspicious attachment id: %llu, %@",
-                                         self.tag,
-                                         (int)statusCode,
-                                         (unsigned long long)attachment.serverId,
-                                         error);
-                                     [DDLog flushLog];
-                                     OWSAssert(0);
-                                 }
-                                 return markAndHandleFailure(error);
-                             }];
+        success:^(NSURLSessionDataTask *task, id responseObject) {
+            if (![responseObject isKindOfClass:[NSDictionary class]]) {
+                DDLogError(@"%@ Failed retrieval of attachment. Response had unexpected format.", self.tag);
+                NSError *error = OWSErrorMakeUnableToProcessServerResponseError();
+                return markAndHandleFailure(error);
+            }
+            NSString *location = [(NSDictionary *)responseObject objectForKey:@"location"];
+            if (!location) {
+                DDLogError(@"%@ Failed retrieval of attachment. Response had no location.", self.tag);
+                NSError *error = OWSErrorMakeUnableToProcessServerResponseError();
+                return markAndHandleFailure(error);
+            }
+
+            dispatch_async([OWSDispatch attachmentsQueue], ^{
+                [self downloadFromLocation:location
+                    pointer:attachment
+                    success:^(NSData *_Nonnull encryptedData) {
+                        [self decryptAttachmentData:encryptedData
+                                            pointer:attachment
+                                            success:markAndHandleSuccess
+                                            failure:markAndHandleFailure];
+                    }
+                    failure:^(NSURLSessionDataTask *_Nullable task, NSError *_Nonnull error) {
+                        if (attachment.serverId < 100) {
+                            // This looks like the symptom of the "frequent 404
+                            // downloading attachments with low server ids".
+                            NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
+                            NSInteger statusCode = [httpResponse statusCode];
+                            DDLogError(@"%@ %d Failure with suspicious attachment id: %llu, %@",
+                                self.tag,
+                                (int)statusCode,
+                                (unsigned long long)attachment.serverId,
+                                error);
+                            [DDLog flushLog];
+                            OWSAssert(0);
+                        }
+                        if (markAndHandleFailure) {
+                            markAndHandleFailure(error);
+                        }
+                    }];
+            });
+        }
+        failure:^(NSURLSessionDataTask *task, NSError *error) {
+            if (!IsNSErrorNetworkFailure(error)) {
+                OWSProdErrorWNSError(@"error_attachment_request_failed", error);
+            }
+            DDLogError(@"Failed retrieval of attachment with error: %@", error);
+            if (attachment.serverId < 100) {
+                // This _shouldn't_ be the symptom of the "frequent 404
+                // downloading attachments with low server ids".
+                NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
+                NSInteger statusCode = [httpResponse statusCode];
+                DDLogError(@"%@ %d Failure with suspicious attachment id: %llu, %@",
+                    self.tag,
+                    (int)statusCode,
+                    (unsigned long long)attachment.serverId,
+                    error);
+                [DDLog flushLog];
+                OWSAssert(0);
+            }
+            return markAndHandleFailure(error);
+        }];
 }
 
 - (void)decryptAttachmentData:(NSData *)cipherText
diff --git a/SignalServiceKit/src/Messages/OWSMessageSender.m b/SignalServiceKit/src/Messages/OWSMessageSender.m
index 3376de51d..65866243a 100644
--- a/SignalServiceKit/src/Messages/OWSMessageSender.m
+++ b/SignalServiceKit/src/Messages/OWSMessageSender.m
@@ -1216,6 +1216,9 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
                 dispatch_semaphore_signal(sema);
             }
             failure:^(NSURLSessionDataTask *task, NSError *error) {
+                if (!IsNSErrorNetworkFailure(error)) {
+                    OWSProdErrorWNSError(@"message_sender_error_recipient_prekey_request_failed", error);
+                }
                 DDLogError(@"Server replied to PreKeyBundle request with error: %@", error);
                 NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
                 if (response.statusCode == 404) {
diff --git a/SignalServiceKit/src/Network/API/OWSDeviceProvisioningCodeService.m b/SignalServiceKit/src/Network/API/OWSDeviceProvisioningCodeService.m
index 1b55f9690..e9198be64 100644
--- a/SignalServiceKit/src/Network/API/OWSDeviceProvisioningCodeService.m
+++ b/SignalServiceKit/src/Network/API/OWSDeviceProvisioningCodeService.m
@@ -1,4 +1,6 @@
-//  Copyright © 2016 Open Whisper Systems. All rights reserved.
+//
+//  Copyright (c) 2017 Open Whisper Systems. All rights reserved.
+//
 
 #import "OWSDeviceProvisioningCodeService.h"
 #import "OWSDeviceProvisioningCodeRequest.h"
@@ -48,6 +50,9 @@ NSString *const OWSDeviceProvisioningCodeServiceProvisioningCodeKey = @"verifica
             }
         }
         failure:^(NSURLSessionDataTask *task, NSError *error) {
+            if (!IsNSErrorNetworkFailure(error)) {
+                OWSProdErrorWNSError(@"error_provisioning_code_request_failed", error);
+            }
             DDLogVerbose(@"ProvisioningCode request failed with error: %@", error);
             failureCallback(error);
         }];
diff --git a/SignalServiceKit/src/Network/API/OWSDeviceProvisioningService.m b/SignalServiceKit/src/Network/API/OWSDeviceProvisioningService.m
index b2b561c1f..fa7c26b7a 100644
--- a/SignalServiceKit/src/Network/API/OWSDeviceProvisioningService.m
+++ b/SignalServiceKit/src/Network/API/OWSDeviceProvisioningService.m
@@ -1,4 +1,6 @@
-//  Copyright © 2016 Open Whisper Systems. All rights reserved.
+//
+//  Copyright (c) 2017 Open Whisper Systems. All rights reserved.
+//
 
 #import "OWSDeviceProvisioningService.h"
 #import "OWSDeviceProvisioningRequest.h"
@@ -45,6 +47,9 @@ NS_ASSUME_NONNULL_BEGIN
             successCallback();
         }
         failure:^(NSURLSessionDataTask *task, NSError *error) {
+            if (!IsNSErrorNetworkFailure(error)) {
+                OWSProdErrorWNSError(@"error_provisioning_request_failed", error);
+            }
             DDLogVerbose(@"Provisioning request failed with error: %@", error);
             failureCallback(error);
         }];
diff --git a/SignalServiceKit/src/Network/API/OWSDevicesService.m b/SignalServiceKit/src/Network/API/OWSDevicesService.m
index f01f42695..e8e83b1df 100644
--- a/SignalServiceKit/src/Network/API/OWSDevicesService.m
+++ b/SignalServiceKit/src/Network/API/OWSDevicesService.m
@@ -1,4 +1,6 @@
-//  Copyright © 2016 Open Whisper Systems. All rights reserved.
+//
+//  Copyright (c) 2017 Open Whisper Systems. All rights reserved.
+//
 
 #import "OWSDevicesService.h"
 #import "OWSDeleteDeviceRequest.h"
@@ -30,6 +32,9 @@ NS_ASSUME_NONNULL_BEGIN
             }
         }
         failure:^(NSURLSessionDataTask *task, NSError *error) {
+            if (!IsNSErrorNetworkFailure(error)) {
+                OWSProdErrorWNSError(@"error_get_devices_failed", error);
+            }
             DDLogVerbose(@"Get devices request failed with error: %@", error);
             failureCallback(error);
         }];
@@ -47,6 +52,9 @@ NS_ASSUME_NONNULL_BEGIN
             successCallback();
         }
         failure:^(NSURLSessionDataTask *task, NSError *error) {
+            if (!IsNSErrorNetworkFailure(error)) {
+                OWSProdErrorWNSError(@"error_unlink_device_failed", error);
+            }
             DDLogVerbose(@"Get devices request failed with error: %@", error);
             failureCallback(error);
         }];
diff --git a/SignalServiceKit/src/Network/API/TSNetworkManager.h b/SignalServiceKit/src/Network/API/TSNetworkManager.h
index aca526709..9c33ac40e 100644
--- a/SignalServiceKit/src/Network/API/TSNetworkManager.h
+++ b/SignalServiceKit/src/Network/API/TSNetworkManager.h
@@ -26,6 +26,8 @@ NS_ASSUME_NONNULL_BEGIN
 
 extern NSString *const TSNetworkManagerDomain;
 
+BOOL IsNSErrorNetworkFailure(NSError *_Nullable error);
+
 @interface TSNetworkManager : NSObject
 
 - (instancetype)init NS_UNAVAILABLE;
diff --git a/SignalServiceKit/src/Network/API/TSNetworkManager.m b/SignalServiceKit/src/Network/API/TSNetworkManager.m
index 1fd8e8232..3b18a7b7b 100644
--- a/SignalServiceKit/src/Network/API/TSNetworkManager.m
+++ b/SignalServiceKit/src/Network/API/TSNetworkManager.m
@@ -12,6 +12,11 @@
 
 NSString *const TSNetworkManagerDomain = @"org.whispersystems.signal.networkManager";
 
+BOOL IsNSErrorNetworkFailure(NSError *_Nullable error)
+{
+    return ([error.domain isEqualToString:TSNetworkManagerDomain] && error.code == 0);
+}
+
 @interface TSNetworkManager ()
 
 typedef void (^failureBlock)(NSURLSessionDataTask *task, NSError *error);