diff --git a/lib/actions/thread-actions.js b/lib/actions/thread-actions.js
--- a/lib/actions/thread-actions.js
+++ b/lib/actions/thread-actions.js
@@ -166,6 +166,11 @@
         };
       } else if (input.changes.avatar && input.changes.avatar.type === 'ens') {
         changes.avatar = { type: 'ens' };
+      } else if (
+        input.changes.avatar &&
+        input.changes.avatar.type === 'remove'
+      ) {
+        changes.avatar = null;
       }
       // To support `image` and `encrypted_image` avatars we first, need stop
       // sending multimedia metadata to keyserver.
diff --git a/lib/shared/dm-ops/change-thread-settings-spec.js b/lib/shared/dm-ops/change-thread-settings-spec.js
--- a/lib/shared/dm-ops/change-thread-settings-spec.js
+++ b/lib/shared/dm-ops/change-thread-settings-spec.js
@@ -55,7 +55,7 @@
     threadInfoUpdate.color = color;
   }
 
-  if (avatar) {
+  if (avatar || avatar === null) {
     threadInfoUpdate.avatar = avatar;
   }
 
@@ -64,9 +64,18 @@
   } = {};
 
   const { avatar: avatarObject, ...rest } = threadInfoUpdate;
-  const normalizedThreadInfoUpdate = avatarObject
-    ? { ...rest, avatar: JSON.stringify(avatarObject) }
-    : { ...rest };
+  let normalizedThreadInfoUpdate;
+  if (avatarObject) {
+    normalizedThreadInfoUpdate = {
+      ...rest,
+      avatar: JSON.stringify(avatarObject),
+    };
+  } else if (avatarObject === null) {
+    // clear thread avatar
+    normalizedThreadInfoUpdate = { ...rest, avatar: '' };
+  } else {
+    normalizedThreadInfoUpdate = { ...rest };
+  }
 
   for (const fieldName in normalizedThreadInfoUpdate) {
     const value = normalizedThreadInfoUpdate[fieldName];
diff --git a/lib/types/dm-ops.js b/lib/types/dm-ops.js
--- a/lib/types/dm-ops.js
+++ b/lib/types/dm-ops.js
@@ -290,7 +290,7 @@
   +name?: string,
   +description?: string,
   +color?: string,
-  +avatar?: ClientAvatar,
+  +avatar?: ClientAvatar | null,
 };
 
 export type DMChangeThreadSettingsOperation = $ReadOnly<{