or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

channels.mdcoroutine-builders.mddispatchers.mdexception-handling.mdflow-api.mdindex.mdjob-management.mdjvm-integration.mdsynchronization.md

exception-handling.mddocs/

0

# Exception Handling

1

2

Structured exception handling with coroutine-aware propagation, cancellation semantics, and supervisor patterns for fault-tolerant concurrent programming.

3

4

## Capabilities

5

6

### CoroutineExceptionHandler

7

8

Context element for handling uncaught exceptions in coroutines.

9

10

```kotlin { .api }

11

interface CoroutineExceptionHandler : CoroutineContext.Element {

12

/** Handles uncaught exception in coroutine context */

13

fun handleException(context: CoroutineContext, exception: Throwable)

14

15

companion object Key : CoroutineContext.Key<CoroutineExceptionHandler>

16

}

17

18

/** Creates exception handler from function */

19

inline fun CoroutineExceptionHandler(

20

crossinline handler: (CoroutineContext, Throwable) -> Unit

21

): CoroutineExceptionHandler

22

```

23

24

**Usage Examples:**

25

26

```kotlin

27

import kotlinx.coroutines.*

28

29

val customExceptionHandler = CoroutineExceptionHandler { context, exception ->

30

println("Caught exception in context $context: ${exception.message}")

31

32

// Log exception, send to crash reporting, etc.

33

when (exception) {

34

is IllegalArgumentException -> println("Invalid argument provided")

35

is RuntimeException -> println("Runtime error occurred")

36

else -> println("Unexpected error: ${exception::class.simpleName}")

37

}

38

}

39

40

fun main() = runBlocking {

41

// Exception handler for top-level coroutines

42

val job = GlobalScope.launch(customExceptionHandler) {

43

throw RuntimeException("Something went wrong!")

44

}

45

46

job.join()

47

48

// Exception handler with multiple coroutines

49

val supervisor = SupervisorJob()

50

val scope = CoroutineScope(Dispatchers.Default + supervisor + customExceptionHandler)

51

52

scope.launch {

53

throw IllegalArgumentException("Invalid input")

54

}

55

56

scope.launch {

57

delay(100)

58

println("This coroutine continues despite sibling failure")

59

}

60

61

delay(200)

62

supervisor.complete()

63

}

64

```

65

66

### SupervisorJob

67

68

Job that doesn't cancel when its children fail, enabling independent failure handling.

69

70

```kotlin { .api }

71

/** Creates supervisor job that isolates child failures */

72

fun SupervisorJob(parent: Job? = null): CompletableJob

73

74

/** Creates supervisor scope with isolated child failures */

75

suspend fun <T> supervisorScope(

76

block: suspend CoroutineScope.() -> T

77

): T

78

```

79

80

**Usage Examples:**

81

82

```kotlin

83

import kotlinx.coroutines.*

84

85

fun main() = runBlocking {

86

// SupervisorJob prevents child failures from cancelling parent

87

val supervisor = SupervisorJob()

88

89

launch(supervisor) {

90

// Child 1 - will fail

91

launch {

92

delay(100)

93

throw RuntimeException("Child 1 failed")

94

}

95

96

// Child 2 - will complete successfully

97

launch {

98

delay(200)

99

println("Child 2 completed successfully")

100

}

101

102

// Child 3 - will also complete

103

launch {

104

delay(300)

105

println("Child 3 completed successfully")

106

}

107

108

delay(500)

109

println("Parent completed - children failures were isolated")

110

}

111

112

delay(600)

113

supervisor.complete()

114

}

115

116

// supervisorScope example for parallel processing

117

suspend fun processItemsSafely(items: List<String>): List<String> = supervisorScope {

118

items.map { item ->

119

async {

120

when {

121

item == "fail" -> throw RuntimeException("Processing failed for $item")

122

item.isEmpty() -> throw IllegalArgumentException("Empty item")

123

else -> "Processed: $item"

124

}

125

}

126

}.mapNotNull { deferred ->

127

try {

128

deferred.await()

129

} catch (e: Exception) {

130

println("Failed to process item: ${e.message}")

131

null // Continue with other items

132

}

133

}

134

}

135

136

suspend fun supervisorScopeExample() {

137

val items = listOf("item1", "fail", "item3", "", "item5")

138

val results = processItemsSafely(items)

139

println("Successfully processed: $results")

140

}

141

```

142

143

### CancellationException

144

145

Special exception used for cooperative cancellation that doesn't propagate to parent.

146

147

```kotlin { .api }

148

/** Exception for cooperative cancellation */

149

open class CancellationException(

150

message: String? = null,

151

cause: Throwable? = null

152

) : IllegalStateException(message)

153

154

/** Throws CancellationException if job is cancelled */

155

fun Job.ensureActive()

156

157

/** Checks if context is active (not cancelled) */

158

val CoroutineContext.isActive: Boolean

159

160

/** Gets current job from context */

161

val CoroutineContext.job: Job

162

```

163

164

**Usage Examples:**

165

166

```kotlin

167

import kotlinx.coroutines.*

168

169

suspend fun cooperativeCancellation() {

170

repeat(1000) { i ->

171

// Check for cancellation periodically

172

if (!coroutineContext.isActive) {

173

println("Detected cancellation at iteration $i")

174

return

175

}

176

177

// Alternative: throws CancellationException if cancelled

178

coroutineContext.ensureActive()

179

180

// Simulate work

181

if (i % 100 == 0) {

182

println("Processing iteration $i")

183

}

184

185

// Suspending functions automatically check for cancellation

186

yield() // Yields execution and checks cancellation

187

}

188

}

189

190

fun main() = runBlocking {

191

val job = launch {

192

try {

193

cooperativeCancellation()

194

println("Completed all iterations")

195

} catch (e: CancellationException) {

196

println("Coroutine was cancelled: ${e.message}")

197

throw e // Re-throw to maintain cancellation semantics

198

}

199

}

200

201

delay(250) // Let it run for a while

202

job.cancel("Manual cancellation")

203

job.join()

204

205

// Custom cancellation exception

206

val customJob = launch {

207

try {

208

delay(1000)

209

} catch (e: CancellationException) {

210

println("Custom cleanup before cancellation")

211

throw e

212

}

213

}

214

215

delay(100)

216

customJob.cancel(CancellationException("Custom cancellation reason"))

217

customJob.join()

218

}

219

```

220

221

### Exception Propagation

222

223

Understanding how exceptions propagate through coroutine hierarchies.

224

225

```kotlin { .api }

226

/** Handles coroutine exception with context */

227

fun handleCoroutineException(context: CoroutineContext, exception: Throwable)

228

229

/** Try-catch equivalents for coroutines */

230

suspend fun <T> runCatching(block: suspend () -> T): Result<T>

231

232

/** Non-cancellable execution block */

233

suspend fun <T> NonCancellable.run(block: suspend () -> T): T

234

```

235

236

**Usage Examples:**

237

238

```kotlin

239

import kotlinx.coroutines.*

240

241

// Exception propagation in regular job hierarchy

242

suspend fun regularJobExceptionPropagation() = coroutineScope {

243

try {

244

val parentJob = launch {

245

launch {

246

delay(100)

247

throw RuntimeException("Child exception")

248

}

249

250

launch {

251

delay(200)

252

println("This won't execute - parent gets cancelled")

253

}

254

255

delay(300)

256

println("Parent won't reach here")

257

}

258

259

parentJob.join()

260

} catch (e: Exception) {

261

println("Caught propagated exception: ${e.message}")

262

}

263

}

264

265

// Exception handling with async

266

suspend fun asyncExceptionHandling() = coroutineScope {

267

val deferred1 = async {

268

delay(100)

269

throw RuntimeException("Async exception")

270

}

271

272

val deferred2 = async {

273

delay(200)

274

"Success result"

275

}

276

277

try {

278

val result1 = deferred1.await() // Exception thrown here

279

println("Won't reach: $result1")

280

} catch (e: Exception) {

281

println("Caught async exception: ${e.message}")

282

}

283

284

try {

285

val result2 = deferred2.await()

286

println("Result2: $result2") // This still works

287

} catch (e: Exception) {

288

println("Unexpected exception: ${e.message}")

289

}

290

}

291

292

// Non-cancellable cleanup

293

suspend fun nonCancellableCleanup() {

294

val resource = "Important Resource"

295

296

try {

297

// Simulate work that gets cancelled

298

repeat(10) { i ->

299

println("Working on iteration $i")

300

delay(100)

301

}

302

} finally {

303

// Ensure cleanup runs even if cancelled

304

withContext(NonCancellable) {

305

println("Cleaning up $resource")

306

delay(50) // Even suspending cleanup operations work

307

println("Cleanup completed")

308

}

309

}

310

}

311

312

fun main() = runBlocking {

313

println("Regular job exception propagation:")

314

regularJobExceptionPropagation()

315

316

println("\nAsync exception handling:")

317

asyncExceptionHandling()

318

319

println("\nNon-cancellable cleanup:")

320

val job = launch {

321

nonCancellableCleanup()

322

}

323

324

delay(250)

325

job.cancel()

326

job.join()

327

}

328

```

329

330

### Error Recovery Patterns

331

332

Common patterns for error recovery and resilience.

333

334

```kotlin { .api }

335

/** Retry with exponential backoff */

336

suspend fun <T> retry(

337

retries: Int,

338

initialDelayMs: Long = 1000,

339

factor: Double = 2.0,

340

block: suspend () -> T

341

): T

342

343

/** Circuit breaker pattern */

344

class CircuitBreaker(

345

val failureThreshold: Int,

346

val recoveryTimeoutMs: Long

347

)

348

```

349

350

**Usage Examples:**

351

352

```kotlin

353

import kotlinx.coroutines.*

354

import kotlin.random.Random

355

356

// Retry with exponential backoff

357

suspend fun <T> retryWithBackoff(

358

retries: Int,

359

initialDelay: Long = 1000,

360

factor: Double = 2.0,

361

block: suspend () -> T

362

): T {

363

var currentDelay = initialDelay

364

repeat(retries) { attempt ->

365

try {

366

return block()

367

} catch (e: Exception) {

368

if (attempt == retries - 1) throw e

369

370

println("Attempt ${attempt + 1} failed: ${e.message}. Retrying in ${currentDelay}ms")

371

delay(currentDelay)

372

currentDelay = (currentDelay * factor).toLong()

373

}

374

}

375

error("Should not reach here")

376

}

377

378

// Circuit breaker implementation

379

class CircuitBreaker(

380

private val failureThreshold: Int,

381

private val recoveryTimeoutMs: Long

382

) {

383

private var failureCount = 0

384

private var lastFailureTime = 0L

385

private var state = State.CLOSED

386

387

enum class State { CLOSED, OPEN, HALF_OPEN }

388

389

suspend fun <T> execute(block: suspend () -> T): T {

390

when (state) {

391

State.CLOSED -> {

392

return try {

393

val result = block()

394

failureCount = 0

395

result

396

} catch (e: Exception) {

397

failureCount++

398

if (failureCount >= failureThreshold) {

399

state = State.OPEN

400

lastFailureTime = System.currentTimeMillis()

401

}

402

throw e

403

}

404

}

405

406

State.OPEN -> {

407

if (System.currentTimeMillis() - lastFailureTime >= recoveryTimeoutMs) {

408

state = State.HALF_OPEN

409

return execute(block)

410

} else {

411

throw RuntimeException("Circuit breaker is OPEN")

412

}

413

}

414

415

State.HALF_OPEN -> {

416

return try {

417

val result = block()

418

state = State.CLOSED

419

failureCount = 0

420

result

421

} catch (e: Exception) {

422

state = State.OPEN

423

lastFailureTime = System.currentTimeMillis()

424

throw e

425

}

426

}

427

}

428

}

429

}

430

431

// Fallback pattern

432

suspend fun <T> withFallback(

433

primary: suspend () -> T,

434

fallback: suspend () -> T

435

): T {

436

return try {

437

primary()

438

} catch (e: Exception) {

439

println("Primary failed: ${e.message}, using fallback")

440

fallback()

441

}

442

}

443

444

fun main() = runBlocking {

445

// Retry example

446

try {

447

val result = retryWithBackoff(retries = 3) {

448

if (Random.nextFloat() < 0.7) {

449

throw RuntimeException("Random failure")

450

}

451

"Success!"

452

}

453

println("Retry result: $result")

454

} catch (e: Exception) {

455

println("All retry attempts failed: ${e.message}")

456

}

457

458

// Circuit breaker example

459

val circuitBreaker = CircuitBreaker(failureThreshold = 3, recoveryTimeoutMs = 1000)

460

461

repeat(10) { i ->

462

try {

463

val result = circuitBreaker.execute {

464

if (i < 5 && Random.nextFloat() < 0.8) {

465

throw RuntimeException("Service failure")

466

}

467

"Service response $i"

468

}

469

println("Circuit breaker result: $result")

470

} catch (e: Exception) {

471

println("Circuit breaker blocked: ${e.message}")

472

}

473

474

delay(200)

475

}

476

477

// Fallback example

478

val result = withFallback(

479

primary = {

480

if (Random.nextBoolean()) throw RuntimeException("Primary service down")

481

"Primary result"

482

},

483

fallback = {

484

"Fallback result"

485

}

486

)

487

println("Fallback result: $result")

488

}

489

```

490

491

## Types

492

493

### Exception Types

494

495

Core exception types for coroutine error handling.

496

497

```kotlin { .api }

498

/** Base exception for cancellation */

499

open class CancellationException : IllegalStateException

500

501

/** Exception for timeout operations */

502

class TimeoutCancellationException : CancellationException

503

504

/** Exception for job cancellation */

505

class JobCancellationException : CancellationException

506

```

507

508

### Context Elements

509

510

Context elements related to exception handling.

511

512

```kotlin { .api }

513

/** Key for CoroutineExceptionHandler in context */

514

object CoroutineExceptionHandler : CoroutineContext.Key<CoroutineExceptionHandler>

515

516

/** Non-cancellable context element */

517

object NonCancellable : AbstractCoroutineContextElement(Job), Job

518

```