Run Blocking In Coroutine

ID

kotlin.run_blocking_in_coroutine

Severity

low

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

Kotlin

Tags

concurrency, reliability

Description

Reports calls to runBlocking { …​ } made from inside another coroutine builder (launch, async, withContext, coroutineScope, supervisorScope, flow, channelFlow, produce, …​).

Rationale

scope.launch {
    runBlocking { delay(100) } // FLAW
}

runBlocking parks the OS thread until its child coroutine completes. Inside another coroutine builder, that thread belongs to the parent’s dispatcher pool — typically a small, shared pool. If the child coroutine needs a thread from the same pool to make progress, the runBlocking deadlocks the whole pool. This is a classic footgun on the Default and IO dispatchers.

Remediation

scope.launch {
    withContext(Dispatchers.IO) { delay(100) } // OK
}

Inside an existing coroutine, never call runBlocking. Call suspending APIs directly, or use withContext(Dispatchers.X) to switch dispatchers without blocking.