diff --git a/lib/shared/markdown.js b/lib/shared/markdown.js
--- a/lib/shared/markdown.js
+++ b/lib/shared/markdown.js
@@ -257,6 +257,41 @@
 const stripSpoilersFromNotifications = (text: string): string =>
   text.replace(replaceSpoilerRegex, spoilerReplacement);
 
+function stripSpoilersFromMarkdownAST(ast: SingleASTNode[]): SingleASTNode[] {
+  // Either takes top-level AST, or array of nodes under an items node (list)
+  return ast.map(replaceSpoilersFromMarkdownAST);
+}
+
+function replaceSpoilersFromMarkdownAST(node: SingleASTNode): SingleASTNode {
+  const { content, items, type } = node;
+  if (content && typeof content === 'string') {
+    // Base case (leaf node)
+    return node;
+  } else if (type === 'spoiler') {
+    // The actual point of this function: replacing the spoilers
+    return {
+      type: 'text',
+      content: spoilerReplacement,
+    };
+  } else if (content) {
+    // Common case... most nodes nest children with content
+    // If content isn't a string, it should be an array
+    return {
+      ...node,
+      content: stripSpoilersFromMarkdownAST(content),
+    };
+  } else if (items) {
+    // Special case for lists, which has a nested array of arrays within items
+    return {
+      ...node,
+      items: items.map(stripSpoilersFromMarkdownAST),
+    };
+  }
+  throw new Error(
+    `unexpected Markdown node of type ${type} with no content or items`,
+  );
+}
+
 export {
   paragraphRegex,
   paragraphStripTrailingNewlineRegex,
@@ -278,4 +313,5 @@
   parseList,
   matchMentions,
   stripSpoilersFromNotifications,
+  stripSpoilersFromMarkdownAST,
 };
diff --git a/lib/shared/messages/text-message-spec.js b/lib/shared/messages/text-message-spec.js
--- a/lib/shared/messages/text-message-spec.js
+++ b/lib/shared/messages/text-message-spec.js
@@ -20,6 +20,7 @@
   type ASTNode,
   type SingleASTNode,
   stripSpoilersFromNotifications,
+  stripSpoilersFromMarkdownAST,
 } from '../markdown';
 import { threadIsGroupChat } from '../thread-utils';
 import { stringForUser } from '../user-utils';
@@ -89,8 +90,9 @@
   messageTitle({ messageInfo, markdownRules }) {
     const { text } = messageInfo;
     const parser = SimpleMarkdown.parserFor(markdownRules);
-    const ast = parser(text, { disableAutoBlockNewlines: true });
-
+    const ast = stripSpoilersFromMarkdownAST(
+      parser(text, { disableAutoBlockNewlines: true }),
+    );
     return getFirstNonQuotedRawLine(ast).trim();
   },