This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
mobilecritical PriorityID: kmp-ios-coroutine-cancellation-propagation

KMP Structured Coroutine Cancellation Propagation on iOS Main RunLoop

Standardizes Swift-Kotlin task cancellation bridging across the Kotlin/Native boundary to prevent unhandled CancellationException termination on the iOS Main RunLoop. Enforces explicit Task.withTaskCancellationHandler lifecycle coupling and translates Kotlin CancellationException into Swift's native CancellationError during high-frequency UI navigation transitions.

Prohibited Anti-Pattern: Leaking Orphaned Kotlin Coroutines and Crashing iOS Runtimes via Unhandled CancellationException Across Swift Concurrency Bridges

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: KMP Structured Coroutine Cancellation Propagation on iOS Main RunLoop - Standardizes Swift-Kotlin task cancellation bridging across the Kotlin/Native boundary to prevent unhandled CancellationException termination on the iOS Main RunLoop. Enforces explicit Task.withTaskCancellationHandler lifecycle coupling and translates Kotlin CancellationException into Swift's native CancellationError during high-frequency UI navigation transitions.
globs: iosApp/**/*.swift, shared/src/commonMain/**/*.kt, shared/src/iosMain/**/*.kt
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/kmp-ios-coroutine-cancellation-propagation"
---

# KMP Structured Coroutine Cancellation Propagation on iOS Main RunLoop

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Leaking Orphaned Kotlin Coroutines and Crashing iOS Runtimes via Unhandled CancellationException Across Swift Concurrency Bridges

## System Constraints
You are a Principal Engineer enforcing strict KMP Structured Coroutine Cancellation Propagation on the iOS Main RunLoop. You mandate that any bridge between Swift Concurrency (Task/async-await) and Kotlin Coroutines explicitly binds the Swift Task cancellation lifecycle to the Kotlin Job via Task.withTaskCancellationHandler or SKIE wrapper abstractions. You forbid swallowing cancellation errors as generic failures, forbid unhandled CancellationException rethrowing into Objective-C interop layers, and require that all state-modifying cancellation sweeps cleanly release resources on Dispatchers.Main.immediate without blocking native UI navigation transitions.

## Strict Guidelines
- Engineers MUST ALWAYS bridge Swift Task cancellation to Kotlin Job cancellation using Task.withTaskCancellationHandler or an established compiler-plugin abstraction (e.g., SKIE).
- Engineers MUST ALWAYS intercept Kotlin CancellationException at the Swift interop boundary and rethrow Swift's native CancellationError() rather than treating it as an application-level NSError or letting it crash the iOS runtime.
- Engineers MUST NOT invoke Kotlin suspend functions from Swift without managing the underlying Disposable/Cancellable handle when not utilizing automatic Swift Concurrency bridges.
- Code SHOULD enforce that UI-bound coroutine scopes in shared code target Dispatchers.Main.immediate to guarantee synchronous cancellation dispatch during iOS navigation teardowns.
- Services MUST ensure shared Flow collectors cleanly complete or close their underlying channels when the consuming Swift AsyncSequence or Combine publisher is cancelled during view pop/dismissal.
- Components NEVER allow unhandled Kotlin exceptions originating from cancelled deferred tasks to escape into the iOS fatal error handler.

## Bad vs Good Implementations
### Bridging Swift Task Cancellation to Kotlin Coroutine Job with CancellationError Mapping

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Fails to register a cancellation handler with Swift Concurrency. When a user pops the view controller during a network fetch, the Swift Task cancels but the Kotlin coroutine keeps executing on the shared pool. When the Kotlin coroutine finally observes cancellation, it throws CancellationException across the ObjC boundary as a generic NSError, failing to trigger Swift's native cancellation flow and polluting UI telemetry.
```swift
// BAD: Direct async bridge execution without task cancellation binding or error translation
func loadUserProfile(userId: String) async throws -> UserProfile {
    do {
        // If the Swift Task is cancelled during navigation pop, the Kotlin coroutine continues running.
        // If Kotlin finishes with CancellationException, it surfaces as an unhandled KotlinException NSError,
        // causing spurious error alerts or fatal runtime unwrapping.
        let profile = try await userViewModel.fetchProfile(id: userId)
        return profile
    } catch {
        // Swallows cancellation or misidentifies it as an active network/domain error
        throw error
    }
}
```

#### Production Standard (Correct)
> **Standard**: Attaches a task cancellation handler via withTaskCancellationHandler so that when Swift cancels the Task (e.g., view pop or navigation transition), the Kotlin job is explicitly cancelled via its handle. It intercepts CancellationException across the interop boundary and maps it directly to Swift's native CancellationError, respecting structured concurrency semantics without triggering unhandled error crashes.
```swift
// GOOD: Explicit cancellation propagation bridging Swift Task to Kotlin Job and mapping CancellationException
func loadUserProfile(userId: String) async throws -> UserProfile {
    let handle = AtomicReference<SharedCommonCancellable?>(nil)
    
    return try await withTaskCancellationHandler {
        try Task.checkCancellation()
        do {
            return try await withCheckedThrowingContinuation { continuation in
                let cancellable = userViewModel.fetchProfileCancellable(id: userId) { profile, error in
                    if let error = error {
                        continuation.resume(throwing: error)
                    } else if let profile = profile {
                        continuation.resume(returning: profile)
                    }
                }
                handle.set(cancellable)
            }
        } catch let nsError as NSError where nsError.isKotlinCancellationException {
            throw CancellationError()
        }
    } onCancel: {
        handle.get()?.cancel()
    }
}

extension NSError {
    var isKotlinCancellationException: Bool {
        domain == "KotlinException" && userInfo["KotlinExceptionType"] as? String == "kotlinx.coroutines.CancellationException"
    }
}
```

Recommended MCP Servers (mcp-serverlist.com)

Equip agent with system tools to verify code for this stack.

Recommended Agent Skills (askills.dev)

Ready-to-use executable skill functions tailored to this stack.

Architectural Specification & Invariant Rationale

Architectural Invariant Design: KMP Structured Coroutine Cancellation Propagation on iOS Main RunLoop

KMP Structured Coroutine Cancellation Propagation on iOS Main RunLoop defines an industrial-grade behavioral standard for mobile engineering. Standardizes Swift-Kotlin task cancellation bridging across the Kotlin/Native boundary to prevent unhandled CancellationException termination on the iOS Main RunLoop. Enforces explicit Task.withTaskCancellationHandler lifecycle coupling and translates Kotlin CancellationException into Swift's native CancellationError during high-frequency UI navigation transitions. Modern software architectures require strict adherence to deterministic patterns to guarantee high availability, maintainability, and zero security vulnerabilities.

Framework Paradigm Shift & Drift MechanicsLLMs are trained on millions of open-source repositories containing outdated code, legacy idioms, and unpatched antipatterns. When coding assistants generate implementations for this stack, they default to common statistical tokens: Leaking Orphaned Kotlin Coroutines and Crashing iOS Runtimes via Unhandled CancellationException Across Swift Concurrency Bridges Without an explicit system instruction file, developers spend hours debugging silent failures.
Failure Modes & Production Blast Radius

Hazard Analysis: What Breaks Without This Rule

The following failure modes have been cataloged from hundreds of hours of production code reviews where AI assistants produced non-compliant implementations:

Failure ModeAI Symptom (The Anti-Pattern)SeverityProduction Impact & Remedy
AI Hallucination & Deprecation DriftLeaking Orphaned Kotlin Coroutines and Crashing iOS Runtimes via Unhandled CancellationException Across Swift Concurrency BridgesCritical
Violating this industrial standard introduces regression bugs, security risks, and unexpected runtime failures in production.
Enforce explicit behavioral system directives across all AI coding assistants in your repository.
Silent Architecture InconsistencyAI assistants produce mixed paradigms across files, creating fragmented codebase conventions.Medium
Increases cognitive load during code reviews and leads to maintenance debt across engineering teams.
Pin project conventions in .cursor/rules, CLAUDE.md, and AGENTS.md so all team agents follow identical guardrails.
Architectural Directives in Detail

Step-by-Step Production Directives

Each rule constraint below is injected into the AI agent's system prompt to establish deterministic boundaries:

1

Engineers MUST ALWAYS bridge Swift Task cancellation to Kotlin Job cancellation using Task.withTaskCancellationHandler or an established compiler-plugin abstraction (e.g., SKIE).

Engineers MUST ALWAYS bridge Swift Task cancellation to Kotlin Job cancellation using Task.withTaskCancellationHandler or an established compiler-plugin abstraction (e.g., SKIE).

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
2

Engineers MUST ALWAYS intercept Kotlin CancellationException at the Swift interop boundary and rethrow Swift's native CancellationError() rather than treating it as an application-level NSError or letting it crash the iOS runtime.

Engineers MUST ALWAYS intercept Kotlin CancellationException at the Swift interop boundary and rethrow Swift's native CancellationError() rather than treating it as an application-level NSError or letting it crash the iOS runtime.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
3

Engineers MUST NOT invoke Kotlin suspend functions from Swift without managing the underlying Disposable/Cancellable handle when not utilizing automatic Swift Concurrency bridges.

Engineers MUST NOT invoke Kotlin suspend functions from Swift without managing the underlying Disposable/Cancellable handle when not utilizing automatic Swift Concurrency bridges.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
4

Code SHOULD enforce that UI-bound coroutine scopes in shared code target Dispatchers.Main.immediate to guarantee synchronous cancellation dispatch during iOS navigation teardowns.

Code SHOULD enforce that UI-bound coroutine scopes in shared code target Dispatchers.Main.immediate to guarantee synchronous cancellation dispatch during iOS navigation teardowns.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
5

Services MUST ensure shared Flow collectors cleanly complete or close their underlying channels when the consuming Swift AsyncSequence or Combine publisher is cancelled during view pop/dismissal.

Services MUST ensure shared Flow collectors cleanly complete or close their underlying channels when the consuming Swift AsyncSequence or Combine publisher is cancelled during view pop/dismissal.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
6

Components NEVER allow unhandled Kotlin exceptions originating from cancelled deferred tasks to escape into the iOS fatal error handler.

Components NEVER allow unhandled Kotlin exceptions originating from cancelled deferred tasks to escape into the iOS fatal error handler.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
Technical Questions & Answers

Frequently Asked Technical Questions

Common questions developers face when configuring AI coding assistants for this stack:

Q:Why is KMP Structured Coroutine Cancellation Propagation on iOS Main RunLoop critical for AI coding assistants?

Standardizes Swift-Kotlin task cancellation bridging across the Kotlin/Native boundary to prevent unhandled CancellationException termination on the iOS Main RunLoop. Enforces explicit Task.withTaskCancellationHandler lifecycle coupling and translates Kotlin CancellationException into Swift's native CancellationError during high-frequency UI navigation transitions. Without explicit behavioral boundaries, AI coding assistants (Cursor, Claude Code, Windsurf, Codex) rely on training data averages, which frequently produce deprecated, inefficient, or vulnerable code patterns.

Q:How does adopting this rule prevent AI hallucinations?

This rule explicitly declares what the AI must avoid (Leaking Orphaned Kotlin Coroutines and Crashing iOS Runtimes via Unhandled CancellationException Across Swift Concurrency Bridges) and prescribes verified positive examples. Grounding the LLM with negative and positive pairs eliminates guesswork.

Q:Which AI coding environments are supported?

This specification is multi-platform compatible: Cursor (.cursor/rules/*.mdc & .cursorrules), Anthropic Claude Code (CLAUDE.md), Windsurf (.windsurfrules), Codex / Cline (AGENTS.md), and GitHub Copilot (.github/copilot-instructions.md).

Q:Can I combine this rule with other technology stacks?

Yes. Use the Interactive Stack Builder or download an Agent Pack to merge this rule with other frontend, backend, database, security, and DevOps rules without syntax collision.

Open Specification · CC BY 4.0·Free for Personal, Team & Commercial In-Repo Use

Citation & Open Source Attribution Guidelines

This specification is licensed under Creative Commons Attribution 4.0 International (CC BY 4.0). You are completely free to adapt and share this rule in your workspace. If citing, including in public AI benchmark datasets, or redistributing, attribution to AgentRules.org is required.

Related MOBILE Rules

View all rules →
1 Rule in Stack

KMP