diff --git a/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerSupport.kt b/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerSupport.kt
index e219c1561..b27794d19 100644
--- a/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerSupport.kt
+++ b/mirai-core/src/commonMain/kotlin/network/handler/NetworkHandlerSupport.kt
@@ -162,6 +162,12 @@ internal abstract class NetworkHandlerSupport(
      *
      * State can only be changed inside [setState].
      *
+     * **IMPORTANT implementation notes:**
+     *
+     * You must create subclasses of [BaseStateImpl] for EVERY SINGLE [NetworkHandler.State].
+     * **DO NOT** use same type for more than one [NetworkHandler.State],
+     * otherwise [setState] will refuse updating state in some concurrent situations and will be very difficult to debug.
+     *
      * **IMPORTANT notes to lifecycle:**
      *
      * Normally if the state is set to [NetworkHandler.State.CLOSED] by [setState], [selector][NetworkHandlerSelector] may reinitialize an instance.
@@ -177,8 +183,11 @@ internal abstract class NetworkHandlerSupport(
         final override val coroutineContext: CoroutineContext =
             this@NetworkHandlerSupport.coroutineContext + Job(this@NetworkHandlerSupport.coroutineContext.job)
 
+        // Important: read the above doc before implementing BaseStateImpl.
+
         // Do not use init blocks to launch anything. Do use [startState]
 
+
         /**
          * Starts things that should be done in this state.
          *
@@ -271,16 +280,14 @@ internal abstract class NetworkHandlerSupport(
     internal val lockForSetStateWithOldInstance = SynchronizedObject()
 
     /**
-     * This can only be called by [setState] or in tests.
-     *
-     * [newType] can be `null` **iff in tests**, to ignore checks.
+     * This can only be called by [setState] or in tests. Note:
      */
     //
     @TestOnly
-    internal fun <S : BaseStateImpl> setStateImpl(newType: KClass<S>?, new: () -> S): S? =
+    internal fun <S : BaseStateImpl> setStateImpl(newType: KClass<S>, new: () -> S): S? =
         lock.withLock {
             val old = _state
-            if (newType != null && old::class == newType) return@withLock null // already set to expected state by another thread. Avoid replications.
+            if (old::class == newType) return@withLock null // already set to expected state by another thread. Avoid replications.
             if (old.correspondingState == NetworkHandler.State.CLOSED) return@withLock null // CLOSED is final.
 
             val stateObserver = context.getOrNull(StateObserver)
diff --git a/mirai-core/src/commonTest/kotlin/network/framework/TestNetworkHandler.kt b/mirai-core/src/commonTest/kotlin/network/framework/TestNetworkHandler.kt
index b2517c6c5..5fa022128 100644
--- a/mirai-core/src/commonTest/kotlin/network/framework/TestNetworkHandler.kt
+++ b/mirai-core/src/commonTest/kotlin/network/framework/TestNetworkHandler.kt
@@ -15,6 +15,7 @@ import kotlinx.coroutines.sync.Mutex
 import kotlinx.coroutines.sync.withLock
 import net.mamoe.mirai.internal.QQAndroidBot
 import net.mamoe.mirai.internal.network.handler.NetworkHandler
+import net.mamoe.mirai.internal.network.handler.NetworkHandler.State.*
 import net.mamoe.mirai.internal.network.handler.NetworkHandlerContext
 import net.mamoe.mirai.internal.network.handler.NetworkHandlerSupport
 import net.mamoe.mirai.internal.network.handler.logger
@@ -32,7 +33,7 @@ internal open class TestNetworkHandler(
     class Connection
 
     @Suppress("EXPOSED_SUPER_CLASS")
-    internal open inner class TestState(
+    internal abstract inner class TestState(
         correspondingState: NetworkHandler.State
     ) : BaseStateImpl(correspondingState) {
         val resumeDeferred = CompletableDeferred<Unit>()
@@ -44,8 +45,8 @@ internal open class TestNetworkHandler(
             resumeCount.incrementAndGet()
             resumeDeferred.complete(Unit)
             when (this.correspondingState) {
-                NetworkHandler.State.INITIALIZED -> {
-                    setState(NetworkHandler.State.CONNECTING)
+                INITIALIZED -> {
+                    setState(CONNECTING)
                 }
                 else -> {
                 }
@@ -57,13 +58,26 @@ internal open class TestNetworkHandler(
         }
     }
 
+    internal inner class TestStateInitial : TestState(INITIALIZED)
+    internal inner class TestStateConnecting : TestState(CONNECTING)
+    internal inner class TestStateLoading : TestState(LOADING)
+    internal inner class TestStateOK : TestState(OK)
+    internal inner class TestStateClosed : TestState(CLOSED)
+
     @OptIn(TestOnly::class)
     fun setState(correspondingState: NetworkHandler.State): TestState? {
         // `null` means ignore checks. All test states have same type TestState.
-        return setStateImpl(null) { TestState(correspondingState) }
+        val state: TestState = when (correspondingState) {
+            INITIALIZED -> TestStateInitial()
+            CONNECTING -> TestStateConnecting()
+            LOADING -> TestStateLoading()
+            OK -> TestStateOK()
+            CLOSED -> TestStateClosed()
+        }
+        return setStateImpl(TestState::class) { state }
     }
 
-    private val initialState = TestState(NetworkHandler.State.INITIALIZED)
+    private val initialState = TestStateInitial()
     override fun initialState(): BaseStateImpl = initialState
 
     val sendPacket get() = ConcurrentLinkedQueue<OutgoingPacket>()
@@ -75,19 +89,19 @@ internal open class TestNetworkHandler(
 
 
     override fun setStateClosed(exception: Throwable?): TestState? {
-        return setState(NetworkHandler.State.CLOSED)
+        return setState(CLOSED)
     }
 
     override fun setStateConnecting(exception: Throwable?): TestState? {
-        return setState(NetworkHandler.State.CONNECTING)
+        return setState(CONNECTING)
     }
 
     override fun setStateOK(conn: Connection, exception: Throwable?): TestState? {
         exception?.printStackTrace()
-        return setState(NetworkHandler.State.OK)
+        return setState(OK)
     }
 
     override fun setStateLoading(conn: Connection): TestState? {
-        return setState(NetworkHandler.State.LOADING)
+        return setState(LOADING)
     }
 }
\ No newline at end of file