diff --git a/web/avatars/emoji-avatar-selection-modal.css b/web/avatars/emoji-avatar-selection-modal.css
--- a/web/avatars/emoji-avatar-selection-modal.css
+++ b/web/avatars/emoji-avatar-selection-modal.css
@@ -31,4 +31,5 @@
   justify-content: center;
   align-items: center;
   min-height: 24px;
+  gap: 4px;
 }
diff --git a/web/avatars/emoji-avatar-selection-modal.react.js b/web/avatars/emoji-avatar-selection-modal.react.js
--- a/web/avatars/emoji-avatar-selection-modal.react.js
+++ b/web/avatars/emoji-avatar-selection-modal.react.js
@@ -7,6 +7,7 @@
 
 import { EditUserAvatarContext } from 'lib/components/base-edit-user-avatar-provider.react.js';
 import { useModalContext } from 'lib/components/modal-provider.react.js';
+import SWMansionIcon from 'lib/components/SWMansionIcon.react.js';
 import {
   defaultAnonymousUserEmojiAvatar,
   getAvatarForUser,
@@ -33,6 +34,8 @@
 
   const { setUserAvatar, userAvatarSaveInProgress } = editUserAvatarContext;
 
+  const [errorMessage, setErrorMessage] = React.useState<?string>();
+
   const currentUserInfo = useSelector(state => state.currentUserInfo);
   const currentUserAvatar: ClientAvatar = getAvatarForUser(currentUserInfo);
   const defaultUserAvatar: ClientEmojiAvatar = currentUserInfo?.username
@@ -62,17 +65,33 @@
   );
 
   const onEmojiSelect = React.useCallback(selection => {
+    setErrorMessage();
     setPendingAvatarEmoji(selection.native);
   }, []);
 
-  const onSaveAvatar = React.useCallback(
-    () => setUserAvatar(pendingEmojiAvatar),
-    [pendingEmojiAvatar, setUserAvatar],
-  );
+  const onColorSelection = React.useCallback((hex: string) => {
+    setErrorMessage();
+    setPendingAvatarColor(hex);
+  }, []);
+
+  const onSaveAvatar = React.useCallback(async () => {
+    try {
+      await setUserAvatar(pendingEmojiAvatar);
+    } catch {
+      setErrorMessage('Avatar update failed. Please try again.');
+    }
+  }, [pendingEmojiAvatar, setUserAvatar]);
 
   let saveButtonContent;
   if (userAvatarSaveInProgress) {
     saveButtonContent = <LoadingIndicator status="loading" size="medium" />;
+  } else if (errorMessage) {
+    saveButtonContent = (
+      <>
+        <SWMansionIcon icon="warning-circle" size={24} />
+        {errorMessage}
+      </>
+    );
   } else {
     saveButtonContent = 'Save Avatar';
   }
@@ -89,13 +108,15 @@
         <div className={css.colorSelectorContainer}>
           <ColorSelector
             currentColor={pendingAvatarColor}
-            onColorSelection={setPendingAvatarColor}
+            onColorSelection={onColorSelection}
           />
         </div>
         <div className={css.saveButtonContainer}>
           <Button
             variant="filled"
-            buttonColor={buttonThemes.standard}
+            buttonColor={
+              errorMessage ? buttonThemes.danger : buttonThemes.standard
+            }
             onClick={onSaveAvatar}
             disabled={userAvatarSaveInProgress}
           >
diff --git a/web/avatars/web-edit-user-avatar-provider.react.js b/web/avatars/web-edit-user-avatar-provider.react.js
--- a/web/avatars/web-edit-user-avatar-provider.react.js
+++ b/web/avatars/web-edit-user-avatar-provider.react.js
@@ -4,8 +4,7 @@
 
 import { BaseEditUserAvatarProvider } from 'lib/components/base-edit-user-avatar-provider.react.js';
 
-const displayAvatarUpdateFailureAlert = () =>
-  alert('Couldn&rsquo;t save avatar. Please try again later.');
+const displayAvatarUpdateFailureAlert = () => null;
 
 // TODO: Implement `selectFromGallery(...)` for `web`.
 const selectFromGallery = async () => null;