diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt
index 8c25df318..04f8d8afd 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt
@@ -416,7 +416,10 @@ internal class MessageChainImplByIterable constructor(
 ) : Message, Iterable<SingleMessage>, MessageChain {
     override val size: Int by lazy { delegate.count() }
     override fun iterator(): Iterator<SingleMessage> = delegate.iterator()
-    override fun toString(): String = this.delegate.joinToString("") { it.toString() }
+    var toStringTemp: String? = null
+    override fun toString(): String =
+        toStringTemp ?: this.delegate.joinToString("") { it.toString() }.also { toStringTemp = it }
+
     override operator fun contains(sub: String): Boolean = delegate.any { it.contains(sub) }
 }
 
@@ -429,7 +432,10 @@ internal class MessageChainImplByCollection constructor(
 ) : Message, Iterable<SingleMessage>, MessageChain {
     override val size: Int get() = delegate.size
     override fun iterator(): Iterator<SingleMessage> = delegate.iterator()
-    override fun toString(): String = this.delegate.joinToString("") { it.toString() }
+    var toStringTemp: String? = null
+    override fun toString(): String =
+        toStringTemp ?: this.delegate.joinToString("") { it.toString() }.also { toStringTemp = it }
+
     override operator fun contains(sub: String): Boolean = delegate.any { it.contains(sub) }
 }
 
@@ -447,7 +453,10 @@ internal class MessageChainImplBySequence constructor(
      */
     private val collected: List<SingleMessage> by lazy { delegate.toList() }
     override fun iterator(): Iterator<SingleMessage> = collected.iterator()
-    override fun toString(): String = this.collected.joinToString("") { it.toString() }
+    var toStringTemp: String? = null
+    override fun toString(): String =
+        toStringTemp ?: this.collected.joinToString("") { it.toString() }.also { toStringTemp = it }
+
     override operator fun contains(sub: String): Boolean = collected.any { it.contains(sub) }
 }