IllegalStateException when launching on Dispatchers.Main: which artifact is actually required?
0 reputation · 15 Jun 2025, 14:24 UTC
0 reputation · 15 Jun 2025, 14:24 UTC
I have a Kotlin project that depends only on kotlinx-coroutines-core, and any coroutine launched on Dispatchers.Main fails at runtime with an IllegalStateException indicating that the Main dispatcher is missing. I understand the core artifact intentionally ships no Main implementation and that platform modules such as kotlinx-coroutines-android or kotlinx-coroutines-swing provide it via service loading.
The uncertainty is about resolution rules rather than the crash itself. My module is consumed both by an Android app and by plain JVM unit tests, so I cannot simply hard-depend on one UI artifact everywhere. I also want to know whether version skew between the core library and the platform dispatcher artifact can silently change which implementation gets loaded, or whether mismatches always surface as this same exception.
Assuming kotlinx-coroutines 1.7.x or newer:
Dispatchers.Main to resolve on each supported target (Android, Swing, JavaFX, JS)?Dispatchers.Main?31775 reputation · 15 Jun 2025, 17:36 UTC
For Kotlin Coroutines 1.7 and newer, Dispatchers.Main is provided by a *platform* artifact that registers a MainDispatcherFactory via ServiceLoader. The core library deliberately contains no Main implementation. The required artifact depends on the target platform:
kotlinx-coroutines-androidkotlinx-coroutines-swingkotlinx-coroutines-javafxkotlinx-coroutines-core-js already includes a Main dispatcher; no extra artifact is needed.Dispatchers.Default, IO, Unconfined) or, for tests, set a test dispatcher with Dispatchers.setMain from kotlinx-coroutines-test.When a module is shared between an Android app and plain JVM unit tests, keep the platform dependency out of the common implementation configuration:
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
// Android source set only
androidMainImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
// JVM test source set only
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3")
}
In your JVM test setup, call:
import kotlinx.coroutines.test.*
@Before
fun setUp() {
Dispatchers.setMain(StandardTestDispatcher())
}
This satisfies Dispatchers.Main without pulling a UI toolkit into the test classpath.
Each platform artifact depends on a specific version of kotlinx-coroutines-core. If Gradle or Maven resolves two different core versions, the MainDispatcherFactory may try to load classes that no longer exist, resulting in:
IllegalStateException: Main dispatcher is not available (most common)NoSuchMethodError or ClassNotFoundExceptionTo avoid this, declare the same version for both artifacts explicitly. Do not rely on transitive resolution to keep them in sync.
./gradlew dependencies --configuration runtimeClasspath (or Maven dependency:tree) and confirm that kotlinx-coroutines-core and the platform artifact share the same version.Dispatchers.Main and observe the exception message; it should mention the missing dispatcher if no factory is loaded.kotlinx-coroutines-test is present and that Dispatchers.setMain is called before any coroutine starts.Could you confirm which build system you use and whether the Android source set already includes kotlinx-coroutines-android while the JVM tests include kotlinx-coroutines-test? This will ensure that the recommendation matches your actual configuration.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.