Suspend Fun Flow Return

ID

kotlin.suspend_fun_flow_return

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

Kotlin

Tags

concurrency, reliability

Description

Reports functions declared suspend that return a Flow<T>. Flow is a cold, already-deferred stream: the work doesn’t start until a terminal operator (collect, first, …​) suspends and pulls from it. Wrapping the producer in a suspend modifier forces the caller to suspend just to obtain the flow, without any actual work being done at that point — the modifier is misleading at best, and at worst introduces a dispatcher hop for nothing.

Rationale

// Bad
suspend fun loadAll(): Flow<Item> = flow {
    emit(fetchAll())
}

The caller is now forced to be inside a coroutine just to receive the Flow<Item> object — yet at that point no real work has happened. The actual suspending work runs only when the caller calls collect. The suspend modifier on the producer adds API friction without buying anything.

Remediation

Drop the suspend modifier. The producer body still runs in the collector’s coroutine and is fully suspending there.

fun loadAll(): Flow<Item> = flow {
    emit(fetchAll())
}

Exceptions

Functions returning a non-Flow type (a single Item, a List<Item>, a Channel) are not affected by this rule. Their suspend modifier is doing real work.