diff --git a/lib/selectors/chat-selectors.js b/lib/selectors/chat-selectors.js
--- a/lib/selectors/chat-selectors.js
+++ b/lib/selectors/chat-selectors.js
@@ -283,21 +283,22 @@
   +threadCreatedFromMessage: ?ThreadInfo,
   +reactions: ReactionInfo,
 };
+export type ComposableChatMessageInfoItem = {
+  +itemType: 'message',
+  +messageInfoType: 'composable',
+  +messageInfo: ComposableMessageInfo,
+  +localMessageInfo: ?LocalMessageInfo,
+  +startsConversation: boolean,
+  +startsCluster: boolean,
+  endsCluster: boolean,
+  +threadCreatedFromMessage: ?ThreadInfo,
+  +reactions: ReactionInfo,
+  +hasBeenEdited: boolean,
+  +isPinned: boolean,
+};
 export type ChatMessageInfoItem =
   | RobotextChatMessageInfoItem
-  | {
-      +itemType: 'message',
-      +messageInfoType: 'composable',
-      +messageInfo: ComposableMessageInfo,
-      +localMessageInfo: ?LocalMessageInfo,
-      +startsConversation: boolean,
-      +startsCluster: boolean,
-      endsCluster: boolean,
-      +threadCreatedFromMessage: ?ThreadInfo,
-      +reactions: ReactionInfo,
-      +hasBeenEdited: boolean,
-      +isPinned: boolean,
-    };
+  | ComposableChatMessageInfoItem;
 export type ChatMessageItem = { itemType: 'loader' } | ChatMessageInfoItem;
 
 export type ReactionInfo = { +[reaction: string]: MessageReactionInfo };
diff --git a/web/chat/composed-message.react.js b/web/chat/composed-message.react.js
--- a/web/chat/composed-message.react.js
+++ b/web/chat/composed-message.react.js
@@ -9,7 +9,7 @@
 } from 'react-feather';
 
 import { useStringForUser } from 'lib/hooks/ens-cache.js';
-import { type ChatMessageInfoItem } from 'lib/selectors/chat-selectors.js';
+import { type ComposableChatMessageInfoItem } from 'lib/selectors/chat-selectors.js';
 import { getMessageLabel } from 'lib/shared/edit-messages-utils.js';
 import { assertComposableMessageType } from 'lib/types/message-types.js';
 import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js';
@@ -48,7 +48,7 @@
 ];
 
 type Props = {
-  +item: ChatMessageInfoItem,
+  +item: ComposableChatMessageInfoItem,
   +threadInfo: ThreadInfo,
   +shouldDisplayPinIndicator: boolean,
   +sendFailed: boolean,
diff --git a/web/chat/edit-message-provider.js b/web/chat/edit-message-provider.js
--- a/web/chat/edit-message-provider.js
+++ b/web/chat/edit-message-provider.js
@@ -4,7 +4,7 @@
 import * as React from 'react';
 
 import ModalOverlay from 'lib/components/modal-overlay.react.js';
-import type { ChatMessageInfoItem } from 'lib/selectors/chat-selectors.js';
+import type { ComposableChatMessageInfoItem } from 'lib/selectors/chat-selectors.js';
 import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js';
 
 import { EditTextMessage } from './edit-text-message.react.js';
@@ -17,7 +17,7 @@
 };
 
 export type EditState = {
-  +messageInfo: ChatMessageInfoItem,
+  +messageInfo: ComposableChatMessageInfoItem,
   +threadInfo: ThreadInfo,
   +editedMessageDraft: ?string,
   +isError: boolean,
diff --git a/web/chat/edit-text-message.react.js b/web/chat/edit-text-message.react.js
--- a/web/chat/edit-text-message.react.js
+++ b/web/chat/edit-text-message.react.js
@@ -5,7 +5,7 @@
 import { useCallback } from 'react';
 import { XCircle as XCircleIcon } from 'react-feather';
 
-import type { ChatMessageInfoItem } from 'lib/selectors/chat-selectors.js';
+import type { ComposableChatMessageInfoItem } from 'lib/selectors/chat-selectors.js';
 import { useEditMessage } from 'lib/shared/edit-messages-utils.js';
 import { trimMessage } from 'lib/shared/message-utils.js';
 import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js';
@@ -19,7 +19,7 @@
 import Button from '../components/button.react.js';
 
 type Props = {
-  +item: ChatMessageInfoItem,
+  +item: ComposableChatMessageInfoItem,
   +threadInfo: ThreadInfo,
   +background: boolean,
 };
diff --git a/web/chat/failed-send.react.js b/web/chat/failed-send.react.js
--- a/web/chat/failed-send.react.js
+++ b/web/chat/failed-send.react.js
@@ -3,7 +3,7 @@
 import invariant from 'invariant';
 import * as React from 'react';
 
-import { type ChatMessageInfoItem } from 'lib/selectors/chat-selectors.js';
+import { type ComposableChatMessageInfoItem } from 'lib/selectors/chat-selectors.js';
 import { threadInfoSelector } from 'lib/selectors/thread-selectors.js';
 import { messageID } from 'lib/shared/message-utils.js';
 import { messageTypes } from 'lib/types/message-types-enum.js';
@@ -21,7 +21,7 @@
 import { useSelector } from '../redux/redux-utils.js';
 
 type BaseProps = {
-  +item: ChatMessageInfoItem,
+  +item: ComposableChatMessageInfoItem,
   +threadInfo: ThreadInfo,
 };
 type Props = {
diff --git a/web/chat/message.react.js b/web/chat/message.react.js
--- a/web/chat/message.react.js
+++ b/web/chat/message.react.js
@@ -36,17 +36,21 @@
 
   let message;
   if (
+    item.messageInfoType === 'composable' &&
     item.messageInfo.id &&
     editState?.messageInfo.messageInfo?.id === item.messageInfo.id
   ) {
     message = (
       <ComposedEditTextMessage
-        item={props.item}
+        item={item}
         threadInfo={props.threadInfo}
         background={true}
       />
     );
-  } else if (item.messageInfo.type === messageTypes.TEXT) {
+  } else if (
+    item.messageInfoType === 'composable' &&
+    item.messageInfo.type === messageTypes.TEXT
+  ) {
     message = (
       <TextMessage
         item={item}
@@ -55,8 +59,9 @@
       />
     );
   } else if (
-    item.messageInfo.type === messageTypes.IMAGES ||
-    item.messageInfo.type === messageTypes.MULTIMEDIA
+    item.messageInfoType === 'composable' &&
+    (item.messageInfo.type === messageTypes.IMAGES ||
+      item.messageInfo.type === messageTypes.MULTIMEDIA)
   ) {
     message = (
       <MultimediaMessage
diff --git a/web/chat/multimedia-message-send-failed.js b/web/chat/multimedia-message-send-failed.js
--- a/web/chat/multimedia-message-send-failed.js
+++ b/web/chat/multimedia-message-send-failed.js
@@ -2,13 +2,13 @@
 
 import invariant from 'invariant';
 
-import type { ChatMessageInfoItem } from 'lib/selectors/chat-selectors.js';
+import type { ComposableChatMessageInfoItem } from 'lib/selectors/chat-selectors.js';
 import { messageTypes } from 'lib/types/message-types-enum.js';
 
 import type { InputState } from '../input/input-state.js';
 
 export default function multimediaMessageSendFailed(
-  item: ChatMessageInfoItem,
+  item: ComposableChatMessageInfoItem,
   inputState: InputState,
 ): boolean {
   const { messageInfo } = item;
diff --git a/web/chat/multimedia-message.react.js b/web/chat/multimedia-message.react.js
--- a/web/chat/multimedia-message.react.js
+++ b/web/chat/multimedia-message.react.js
@@ -7,7 +7,7 @@
   encryptedMediaBlobURI,
   encryptedVideoThumbnailBlobURI,
 } from 'lib/media/media-utils.js';
-import { type ChatMessageInfoItem } from 'lib/selectors/chat-selectors.js';
+import { type ComposableChatMessageInfoItem } from 'lib/selectors/chat-selectors.js';
 import { messageTypes } from 'lib/types/message-types-enum.js';
 import type { ThreadInfo } from 'lib/types/minimally-encoded-thread-permissions-types.js';
 
@@ -18,7 +18,7 @@
 import Multimedia from '../media/multimedia.react.js';
 
 type BaseProps = {
-  +item: ChatMessageInfoItem,
+  +item: ComposableChatMessageInfoItem,
   +threadInfo: ThreadInfo,
   +shouldDisplayPinIndicator: boolean,
 };
diff --git a/web/chat/text-message-send-failed.js b/web/chat/text-message-send-failed.js
--- a/web/chat/text-message-send-failed.js
+++ b/web/chat/text-message-send-failed.js
@@ -1,11 +1,11 @@
 // @flow
 
-import type { ChatMessageInfoItem } from 'lib/selectors/chat-selectors.js';
+import type { ComposableChatMessageInfoItem } from 'lib/selectors/chat-selectors.js';
 import { textMessageSendFailed as sharedTextMessageSendFailed } from 'lib/shared/chat-utils.js';
 import { messageTypes } from 'lib/types/message-types-enum.js';
 
 export default function textMessageSendFailed(
-  item: ChatMessageInfoItem,
+  item: ComposableChatMessageInfoItem,
 ): boolean {
   const { messageInfo, localMessageInfo } = item;
 
diff --git a/web/chat/text-message.react.js b/web/chat/text-message.react.js
--- a/web/chat/text-message.react.js
+++ b/web/chat/text-message.react.js
@@ -4,7 +4,7 @@
 import invariant from 'invariant';
 import * as React from 'react';
 
-import type { ChatMessageInfoItem } from 'lib/selectors/chat-selectors.js';
+import type { ComposableChatMessageInfoItem } from 'lib/selectors/chat-selectors.js';
 import { colorIsDark } from 'lib/shared/color-utils.js';
 import { onlyEmojiRegex } from 'lib/shared/emojis.js';
 import { messageTypes } from 'lib/types/message-types-enum.js';
@@ -17,7 +17,7 @@
 import Markdown from '../markdown/markdown.react.js';
 
 type Props = {
-  +item: ChatMessageInfoItem,
+  +item: ComposableChatMessageInfoItem,
   +threadInfo: ThreadInfo,
   +shouldDisplayPinIndicator: boolean,
 };
diff --git a/web/tooltips/tooltip-action-utils.js b/web/tooltips/tooltip-action-utils.js
--- a/web/tooltips/tooltip-action-utils.js
+++ b/web/tooltips/tooltip-action-utils.js
@@ -325,6 +325,10 @@
     if (!canEditMessage) {
       return null;
     }
+    invariant(
+      item.messageInfoType === 'composable',
+      'canEditMessage should only be true for composable messages!',
+    );
     const buttonContent = <CommIcon icon="edit-filled" size={18} />;
     const onClickEdit = () => {
       const callback = (maxHeight: number) =>