Suspend Coroutine No Cancel

ID

kotlin.suspend_coroutine_no_cancel

Severity

low

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

Kotlin

Tags

concurrency, reliability

Description

Reports usages of suspendCoroutine { …​ }, recommending suspendCancellableCoroutine instead so cancellation can propagate to the underlying callback.

Rationale

suspend fun fetch(): String = suspendCoroutine { cont -> // FLAW
    api.fetch { result -> cont.resume(result) }
}

suspendCoroutine bridges a callback-based API to a coroutine, but the returned continuation is not cancellable. When the surrounding coroutine is cancelled the callback keeps running and the coroutine waits for it (or never returns if the callback never fires). This breaks structured concurrency and leaks resources held by the callback.

Remediation

suspend fun fetch(): String = suspendCancellableCoroutine { cont ->
    val handle = api.fetch { result -> cont.resume(result) }
    cont.invokeOnCancellation { handle.cancel() }
}

Use suspendCancellableCoroutine and wire invokeOnCancellation { } to the underlying resource’s cancel/dispose hook.