diff --git a/lib/actions/user-actions.js b/lib/actions/user-actions.js
--- a/lib/actions/user-actions.js
+++ b/lib/actions/user-actions.js
@@ -132,7 +132,9 @@
         if (!usingCommServicesAccessToken) {
           return;
         }
-        invariant(identityClient, 'Identity client should be set');
+        if (!identityClient) {
+          throw new Error('Identity service client is not initialized');
+        }
         try {
           await Promise.race([
             identityClient.logOut(),
@@ -168,6 +170,42 @@
   );
 }
 
+function useIdentityLogOut(): () => Promise<LogOutResult> {
+  const client = React.useContext(IdentityClientContext);
+  const identityClient = client?.identityClient;
+
+  const preRequestUserState = usePreRequestUserState();
+  const commServicesAccessToken = useSelector(
+    state => state.commServicesAccessToken,
+  );
+
+  return React.useCallback(async () => {
+    invariant(
+      usingCommServicesAccessToken,
+      'identityLogOut can only be called when usingCommServicesAccessToken',
+    );
+    if (!identityClient) {
+      throw new Error('Identity service client is not initialized');
+    }
+    try {
+      await Promise.race([
+        identityClient.logOut(),
+        (async () => {
+          await sleep(500);
+          throw new Error('identity log_out took more than 500ms');
+        })(),
+      ]);
+    } catch {}
+    return {
+      currentUserInfo: null,
+      preRequestUserState: {
+        ...preRequestUserState,
+        commServicesAccessToken,
+      },
+    };
+  }, [commServicesAccessToken, identityClient, preRequestUserState]);
+}
+
 const claimUsernameActionTypes = Object.freeze({
   started: 'CLAIM_USERNAME_STARTED',
   success: 'CLAIM_USERNAME_SUCCESS',
@@ -285,6 +323,54 @@
   ]);
 }
 
+// Unlike useDeleteAccount, we always dispatch a success here (never throw).
+// That's because useDeleteAccount is used in a scenario where the user is
+// visibly logged-in, and we don't want to log them out unless we succeeded in
+// deleting their account. On the other hand, useDeleteDiscardedIdentityAccount
+// is used in a scenario where the user is visibly logged-out, and it's only
+// used to reset state (eg. Redux, SQLite) to a logged-out state. The state
+// reset only occurs when a success action is dispatched, so we always dispatch
+// a success.
+function useDeleteDiscardedIdentityAccount(): () => Promise<LogOutResult> {
+  const client = React.useContext(IdentityClientContext);
+  const identityClient = client?.identityClient;
+
+  const preRequestUserState = usePreRequestUserState();
+  const commServicesAccessToken = useSelector(
+    state => state.commServicesAccessToken,
+  );
+
+  return React.useCallback(async () => {
+    invariant(
+      usingCommServicesAccessToken,
+      'deleteDiscardedIdentityAccount can only be called when ' +
+        'usingCommServicesAccessToken',
+    );
+    if (!identityClient) {
+      throw new Error('Identity service client is not initialized');
+    }
+    if (!identityClient.deleteWalletUser) {
+      throw new Error('Delete wallet user method unimplemented');
+    }
+    try {
+      await Promise.race([
+        identityClient.deleteWalletUser(),
+        (async () => {
+          await sleep(500);
+          throw new Error('identity delete_wallet_user took more than 500ms');
+        })(),
+      ]);
+    } catch {}
+    return {
+      currentUserInfo: null,
+      preRequestUserState: {
+        ...preRequestUserState,
+        commServicesAccessToken,
+      },
+    };
+  }, [commServicesAccessToken, identityClient, preRequestUserState]);
+}
+
 const legacyKeyserverRegisterActionTypes = Object.freeze({
   started: 'LEGACY_KEYSERVER_REGISTER_STARTED',
   success: 'LEGACY_KEYSERVER_REGISTER_SUCCESS',
@@ -871,6 +957,7 @@
   useLegacyLogIn,
   legacyLogInActionTypes,
   useLogOut,
+  useIdentityLogOut,
   logOutActionTypes,
   legacyKeyserverRegister,
   legacyKeyserverRegisterActionTypes,
@@ -888,6 +975,7 @@
   updateUserAvatar,
   deleteAccountActionTypes,
   useDeleteAccount,
+  useDeleteDiscardedIdentityAccount,
   keyserverAuthActionTypes,
   keyserverAuth as keyserverAuthRawAction,
   identityRegisterActionTypes,
diff --git a/lib/hooks/login-hooks.js b/lib/hooks/login-hooks.js
--- a/lib/hooks/login-hooks.js
+++ b/lib/hooks/login-hooks.js
@@ -7,6 +7,8 @@
   identityLogInActionTypes,
   useIdentityPasswordLogIn,
   useIdentityWalletLogIn,
+  logOutActionTypes,
+  useIdentityLogOut,
 } from '../actions/user-actions.js';
 import { useKeyserverAuth } from '../keyserver-conn/keyserver-auth.js';
 import { logInActionSources } from '../types/account-types.js';
@@ -98,6 +100,10 @@
       !state.currentUserInfo.anonymous,
   );
 
+  // We call identityLogOut in order to reset state if identity auth succeeds
+  // but authoritative keyserver auth fails
+  const identityLogOut = useIdentityLogOut();
+
   const registeringOnAuthoritativeKeyserverRef = React.useRef(false);
   const dispatch = useDispatch();
   React.useEffect(() => {
@@ -128,13 +134,21 @@
         });
         resolve();
       } catch (e) {
+        void dispatchActionPromise(logOutActionTypes, identityLogOut());
         reject(e);
       } finally {
         setCurrentStep(inactiveStep);
         registeringOnAuthoritativeKeyserverRef.current = false;
       }
     })();
-  }, [currentStep, isRegisteredOnIdentity, keyserverAuth, dispatch]);
+  }, [
+    currentStep,
+    isRegisteredOnIdentity,
+    keyserverAuth,
+    dispatch,
+    dispatchActionPromise,
+    identityLogOut,
+  ]);
 
   return returnedFunc;
 }
diff --git a/native/account/registration/registration-server-call.js b/native/account/registration/registration-server-call.js
--- a/native/account/registration/registration-server-call.js
+++ b/native/account/registration/registration-server-call.js
@@ -9,6 +9,8 @@
   legacyKeyserverRegister,
   useIdentityPasswordRegister,
   identityRegisterActionTypes,
+  deleteAccountActionTypes,
+  useDeleteDiscardedIdentityAccount,
 } from 'lib/actions/user-actions.js';
 import { useKeyserverAuth } from 'lib/keyserver-conn/keyserver-auth.js';
 import { useLegacyAshoatKeyserverCall } from 'lib/keyserver-conn/legacy-keyserver-call.js';
@@ -314,6 +316,10 @@
       !state.currentUserInfo.anonymous,
   );
 
+  // We call deleteDiscardedIdentityAccount in order to reset state if identity
+  // registration succeeds but authoritative keyserver auth fails
+  const deleteDiscardedIdentityAccount = useDeleteDiscardedIdentityAccount();
+
   const registeringOnAuthoritativeKeyserverRef = React.useRef(false);
   React.useEffect(() => {
     if (
@@ -349,14 +355,37 @@
           resolve,
           reject,
         });
-      } catch (e) {
-        reject(e);
+      } catch (keyserverAuthException) {
+        const discardIdentityAccountPromise = (async () => {
+          try {
+            return await deleteDiscardedIdentityAccount();
+          } catch (deleteException) {
+            Alert.alert(
+              'Account created but login failed',
+              'We were able to create your account, but were unable to log ' +
+                'you in. Try going back to the login screen and logging in ' +
+                'with your new credentials.',
+            );
+            throw deleteException;
+          }
+        })();
+        void dispatchActionPromise(
+          deleteAccountActionTypes,
+          discardIdentityAccountPromise,
+        );
+        reject(keyserverAuthException);
         setCurrentStep(inactiveStep);
       } finally {
         registeringOnAuthoritativeKeyserverRef.current = false;
       }
     })();
-  }, [currentStep, isRegisteredOnIdentity, keyserverAuth]);
+  }, [
+    currentStep,
+    isRegisteredOnIdentity,
+    keyserverAuth,
+    dispatchActionPromise,
+    deleteDiscardedIdentityAccount,
+  ]);
 
   // STEP 3: SETTING AVATAR