or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

console-io.mdexceptions.mdindex.mdrandom.mdreflection.mdtime.mduuid.md

exceptions.mddocs/

0

# Exception Handling

1

2

## Overview

3

4

The Exception Handling capabilities provide WASI-specific implementations for Kotlin's exception system. The implementation includes the base `Throwable` class with limited stack trace support and comprehensive error code mapping for WASI-specific system call failures.

5

6

## API Reference

7

8

### Throwable Base Class

9

10

```kotlin { .api }

11

/**

12

* The base class for all errors and exceptions in WASI environment.

13

* Provides limited stack trace support due to WebAssembly constraints.

14

*/

15

actual class Throwable {

16

/**

17

* Creates a throwable with no message and no cause.

18

*/

19

actual constructor()

20

21

/**

22

* Creates a throwable with the specified message.

23

* @param message the detail message

24

*/

25

actual constructor(message: String?)

26

27

/**

28

* Creates a throwable with the specified cause.

29

* @param cause the cause of this throwable

30

*/

31

actual constructor(cause: Throwable?)

32

33

/**

34

* Creates a throwable with the specified message and cause.

35

* @param message the detail message

36

* @param cause the cause of this throwable

37

*/

38

actual constructor(message: String?, cause: Throwable?)

39

40

/**

41

* The detail message of this throwable.

42

*/

43

actual val message: String?

44

45

/**

46

* The cause of this throwable, or null if the cause is unknown.

47

*/

48

actual val cause: Throwable?

49

}

50

```

51

52

### WASI Error Handling

53

54

```kotlin { .api }

55

/**

56

* Internal WASI error codes from preview1 specification.

57

* Used for mapping WASI system call errors to Kotlin exceptions.

58

*/

59

internal enum class WasiErrorCode {

60

SUCCESS, // No error occurred

61

E2BIG, // Argument list too long

62

EACCES, // Permission denied

63

EADDRINUSE, // Address already in use

64

EADDRNOTAVAIL, // Address not available

65

EAFNOSUPPORT, // Address family not supported

66

EAGAIN, // Resource temporarily unavailable

67

EALREADY, // Connection already in progress

68

EBADF, // Bad file descriptor

69

EBADMSG, // Bad message

70

EBUSY, // Device or resource busy

71

ECANCELED, // Operation canceled

72

ECHILD, // No child processes

73

ECONNABORTED, // Connection aborted

74

ECONNREFUSED, // Connection refused

75

ECONNRESET, // Connection reset

76

EDEADLK, // Resource deadlock avoided

77

EDESTADDRREQ, // Destination address required

78

EDOM, // Mathematics argument out of domain

79

EDQUOT, // Disk quota exceeded

80

EEXIST, // File exists

81

EFAULT, // Bad address

82

EFBIG, // File too large

83

EHOSTUNREACH, // Host is unreachable

84

EIDRM, // Identifier removed

85

EILSEQ, // Invalid or incomplete multibyte sequence

86

EINPROGRESS, // Operation in progress

87

EINTR, // Interrupted system call

88

EINVAL, // Invalid argument

89

EIO, // Input/output error

90

EISCONN, // Socket is connected

91

EISDIR, // Is a directory

92

ELOOP, // Symbolic link loop

93

EMFILE, // Too many open files

94

EMLINK, // Too many links

95

EMSGSIZE, // Message too large

96

EMULTIHOP, // Multihop attempted

97

ENAMETOOLONG, // File name too long

98

ENETDOWN, // Network is down

99

ENETRESET, // Network dropped connection on reset

100

ENETUNREACH, // Network is unreachable

101

ENFILE, // Too many open files in system

102

ENOBUFS, // No buffer space available

103

ENODEV, // No such device

104

ENOENT, // No such file or directory

105

ENOEXEC, // Executable file format error

106

ENOLCK, // No locks available

107

ENOLINK, // Link has been severed

108

ENOMEM, // Cannot allocate memory

109

ENOMSG, // No message of desired type

110

ENOPROTOOPT, // Protocol not available

111

ENOSPC, // No space left on device

112

ENOSYS, // Function not implemented

113

ENOTCONN, // Socket is not connected

114

ENOTDIR, // Not a directory

115

ENOTEMPTY, // Directory not empty

116

ENOTRECOVERABLE, // State not recoverable

117

ENOTSOCK, // Not a socket

118

ENOTSUP, // Operation not supported

119

ENOTTY, // Inappropriate I/O control operation

120

ENXIO, // No such device or address

121

EOVERFLOW, // Value too large for data type

122

EOWNERDEAD, // Previous owner died

123

EPERM, // Operation not permitted

124

EPIPE, // Broken pipe

125

EPROTO, // Protocol error

126

EPROTONOSUPPORT, // Protocol not supported

127

EPROTOTYPE, // Protocol wrong type for socket

128

ERANGE, // Result too large

129

EROFS, // Read-only file system

130

ESPIPE, // Invalid seek

131

ESRCH, // No such process

132

ESTALE, // Stale file handle

133

ETIMEDOUT, // Connection timed out

134

ETXTBSY, // Text file busy

135

EXDEV, // Invalid cross-device link

136

ENOTCAPABLE // Capability insufficient

137

}

138

139

/**

140

* Internal exception class for WASI-specific errors.

141

* Maps WASI error codes to Kotlin exceptions with appropriate messages.

142

*/

143

internal class WasiError(

144

val errorCode: WasiErrorCode,

145

message: String = errorCode.name

146

) : Exception(message)

147

```

148

149

## Usage Examples

150

151

### Basic Exception Handling

152

153

```kotlin

154

// Throwing and catching exceptions

155

fun riskyOperation() {

156

throw RuntimeException("Something went wrong")

157

}

158

159

try {

160

riskyOperation()

161

} catch (e: RuntimeException) {

162

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

163

println("Cause: ${e.cause}")

164

}

165

```

166

167

### Custom Exception Classes

168

169

```kotlin

170

// Create custom exception types

171

class ConfigurationException(

172

message: String,

173

cause: Throwable? = null

174

) : Exception(message, cause)

175

176

class ValidationException(

177

val field: String,

178

message: String

179

) : Exception("Validation failed for field '$field': $message")

180

181

// Usage

182

fun validateUser(user: User) {

183

if (user.name.isBlank()) {

184

throw ValidationException("name", "Name cannot be blank")

185

}

186

if (user.email.contains("@").not()) {

187

throw ValidationException("email", "Invalid email format")

188

}

189

}

190

191

try {

192

validateUser(User("", "invalid-email"))

193

} catch (e: ValidationException) {

194

println("Validation error in ${e.field}: ${e.message}")

195

}

196

```

197

198

### Exception Chaining

199

200

```kotlin

201

// Chain exceptions to preserve error context

202

class DatabaseService {

203

fun saveUser(user: User) {

204

try {

205

// Simulate database operation

206

performDatabaseOperation(user)

207

} catch (e: Exception) {

208

throw RuntimeException("Failed to save user: ${user.name}", e)

209

}

210

}

211

212

private fun performDatabaseOperation(user: User) {

213

// Simulate a lower-level exception

214

throw IllegalStateException("Database connection lost")

215

}

216

}

217

218

// Usage

219

val service = DatabaseService()

220

try {

221

service.saveUser(User("Alice", "alice@example.com"))

222

} catch (e: RuntimeException) {

223

println("Top-level error: ${e.message}")

224

println("Root cause: ${e.cause?.message}")

225

226

// Walk the exception chain

227

var current: Throwable? = e

228

var depth = 0

229

while (current != null) {

230

println(" ${" ".repeat(depth)}${current::class.simpleName}: ${current.message}")

231

current = current.cause

232

depth++

233

}

234

}

235

```

236

237

### Resource Management with Exceptions

238

239

```kotlin

240

// Safe resource management with exception handling

241

class ResourceManager : AutoCloseable {

242

private var closed = false

243

244

fun performOperation() {

245

if (closed) {

246

throw IllegalStateException("Resource is closed")

247

}

248

// Simulate operation that might fail

249

if (Random.nextBoolean()) {

250

throw RuntimeException("Operation failed randomly")

251

}

252

println("Operation completed successfully")

253

}

254

255

override fun close() {

256

if (!closed) {

257

closed = true

258

println("Resource closed")

259

}

260

}

261

}

262

263

// Use with try-with-resources pattern

264

fun useResource() {

265

try {

266

ResourceManager().use { resource ->

267

resource.performOperation()

268

resource.performOperation()

269

}

270

} catch (e: Exception) {

271

println("Operation failed: ${e.message}")

272

}

273

}

274

```

275

276

### Error Recovery Patterns

277

278

```kotlin

279

// Retry with exponential backoff

280

suspend fun retryWithBackoff(

281

maxRetries: Int = 3,

282

baseDelayMs: Long = 1000,

283

operation: suspend () -> Unit

284

) {

285

var lastException: Exception? = null

286

287

repeat(maxRetries) { attempt ->

288

try {

289

operation()

290

return // Success

291

} catch (e: Exception) {

292

lastException = e

293

if (attempt < maxRetries - 1) {

294

val delay = baseDelayMs * (1L shl attempt) // Exponential backoff

295

println("Attempt ${attempt + 1} failed, retrying in ${delay}ms: ${e.message}")

296

kotlinx.coroutines.delay(delay)

297

}

298

}

299

}

300

301

// All retries failed

302

throw RuntimeException("Operation failed after $maxRetries attempts", lastException)

303

}

304

305

// Circuit breaker pattern

306

class CircuitBreaker(

307

private val failureThreshold: Int = 5,

308

private val recoveryTimeMs: Long = 60000

309

) {

310

private var failures = 0

311

private var lastFailureTime = 0L

312

private var state = State.CLOSED

313

314

enum class State { CLOSED, OPEN, HALF_OPEN }

315

316

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

317

when (state) {

318

State.OPEN -> {

319

if (System.currentTimeMillis() - lastFailureTime > recoveryTimeMs) {

320

state = State.HALF_OPEN

321

} else {

322

throw RuntimeException("Circuit breaker is OPEN")

323

}

324

}

325

State.HALF_OPEN -> {

326

// Allow one test call

327

}

328

State.CLOSED -> {

329

// Normal operation

330

}

331

}

332

333

try {

334

val result = operation()

335

// Success - reset circuit breaker

336

failures = 0

337

state = State.CLOSED

338

return result

339

} catch (e: Exception) {

340

failures++

341

lastFailureTime = System.currentTimeMillis()

342

343

if (failures >= failureThreshold) {

344

state = State.OPEN

345

}

346

throw e

347

}

348

}

349

}

350

```

351

352

### WASI System Call Error Handling

353

354

```kotlin

355

// Handle WASI-specific errors (internal implementation detail)

356

internal fun handleWasiError(errorCode: Int, operation: String) {

357

if (errorCode != 0) {

358

val wasiError = WasiErrorCode.values().getOrNull(errorCode)

359

val message = when (wasiError) {

360

WasiErrorCode.EBADF -> "Bad file descriptor in $operation"

361

WasiErrorCode.EIO -> "I/O error during $operation"

362

WasiErrorCode.ENOSPC -> "No space left on device during $operation"

363

WasiErrorCode.EPIPE -> "Broken pipe during $operation"

364

WasiErrorCode.EFAULT -> "Bad address during $operation"

365

WasiErrorCode.EINVAL -> "Invalid argument in $operation"

366

WasiErrorCode.ENOSYS -> "Function not implemented: $operation"

367

else -> "WASI error $errorCode during $operation"

368

}

369

throw WasiError(wasiError ?: WasiErrorCode.SUCCESS, message)

370

}

371

}

372

373

// Example usage in I/O operations

374

fun safeWriteToConsole(message: String) {

375

try {

376

println(message)

377

} catch (e: WasiError) {

378

when (e.errorCode) {

379

WasiErrorCode.EPIPE -> {

380

// Output stream closed, log to alternative location

381

System.err.println("Console output unavailable: ${e.message}")

382

}

383

WasiErrorCode.EIO -> {

384

// I/O error, retry once

385

try {

386

Thread.sleep(100)

387

println(message)

388

} catch (retryException: Exception) {

389

System.err.println("Failed to write after retry: ${retryException.message}")

390

}

391

}

392

else -> {

393

System.err.println("Unexpected WASI error: ${e.message}")

394

}

395

}

396

}

397

}

398

```

399

400

## Implementation Details

401

402

### Limited Stack Trace Support

403

404

Due to WebAssembly constraints, stack traces in WASI have limitations:

405

406

- **No Native Stack Walking**: WebAssembly doesn't support native stack trace generation

407

- **Minimal Debug Information**: Stack traces may contain limited function names

408

- **Platform Dependent**: Stack trace quality depends on the WASI runtime implementation

409

410

### Memory-Efficient Exception Handling

411

412

```kotlin

413

// Exceptions are designed for minimal memory overhead

414

val exception = RuntimeException("Error message")

415

// Only stores message and cause references, no heavyweight stack trace data

416

```

417

418

### Error Code Mapping

419

420

WASI system call errors are comprehensively mapped:

421

422

- **82 Error Codes**: All WASI preview1 error codes are supported

423

- **Appropriate Exceptions**: WASI errors are mapped to suitable Kotlin exception types

424

- **Contextual Messages**: Error messages include operation context for better debugging

425

426

## Performance Considerations

427

428

### Exception Creation Cost

429

430

```kotlin

431

// Exceptions are lightweight in WASI

432

val quickException = RuntimeException("Fast creation")

433

434

// Avoid creating exceptions in hot paths when possible

435

fun validateInput(value: String): Boolean {

436

// Prefer returning boolean over throwing exception for validation

437

return value.isNotBlank()

438

}

439

440

// Use exceptions for exceptional cases

441

fun processInput(value: String) {

442

if (!validateInput(value)) {

443

throw IllegalArgumentException("Input cannot be blank")

444

}

445

// Process valid input

446

}

447

```

448

449

### Exception Handling Overhead

450

451

```kotlin

452

// Exception handling has minimal overhead in WASI

453

try {

454

riskyOperation()

455

} catch (e: Exception) {

456

// Minimal catch overhead

457

handleError(e)

458

}

459

460

// However, avoid exceptions for control flow

461

// Bad:

462

fun findUser(id: String): User {

463

try {

464

return users[id] ?: throw NotFoundException()

465

} catch (e: NotFoundException) {

466

return createDefaultUser()

467

}

468

}

469

470

// Good:

471

fun findUser(id: String): User {

472

return users[id] ?: createDefaultUser()

473

}

474

```

475

476

## Best Practices

477

478

### Exception Design

479

480

```kotlin

481

// DO: Create specific exception types for different error conditions

482

class UserNotFoundException(userId: String) : Exception("User not found: $userId")

483

class InvalidCredentialsException : Exception("Invalid username or password")

484

485

// DO: Include relevant context in exception messages

486

class ProcessingException(

487

val stage: String,

488

val itemId: String,

489

cause: Throwable

490

) : Exception("Processing failed at stage '$stage' for item '$itemId'", cause)

491

492

// DON'T: Use generic exceptions for specific problems

493

// throw Exception("Something went wrong") // Too generic

494

```

495

496

### Error Recovery

497

498

```kotlin

499

// DO: Handle exceptions at appropriate levels

500

class UserService {

501

fun createUser(userData: UserData): User {

502

try {

503

validateUserData(userData)

504

return saveUser(userData)

505

} catch (e: ValidationException) {

506

// Handle validation errors specifically

507

throw UserCreationException("Invalid user data", e)

508

} catch (e: DatabaseException) {

509

// Handle database errors specifically

510

throw UserCreationException("Database error during user creation", e)

511

}

512

}

513

}

514

515

// DO: Use finally blocks for cleanup

516

fun processFile(filename: String) {

517

var resource: FileResource? = null

518

try {

519

resource = openFile(filename)

520

processResource(resource)

521

} catch (e: IOException) {

522

logger.error("File processing failed", e)

523

throw ProcessingException("Failed to process file: $filename", e)

524

} finally {

525

resource?.close()

526

}

527

}

528

```

529

530

### Logging and Monitoring

531

532

```kotlin

533

// Good exception logging practices

534

class ErrorHandler {

535

fun handleError(operation: String, error: Throwable) {

536

// Log with appropriate level

537

when (error) {

538

is ValidationException -> {

539

logger.warn("Validation failed in $operation: ${error.message}")

540

}

541

is SecurityException -> {

542

logger.error("Security violation in $operation", error)

543

}

544

else -> {

545

logger.error("Unexpected error in $operation", error)

546

}

547

}

548

549

// Include relevant context

550

logger.error("Error context - Operation: $operation, Time: ${Instant.now()}")

551

552

// Log exception chain

553

var cause = error.cause

554

while (cause != null) {

555

logger.error("Caused by: ${cause::class.simpleName}: ${cause.message}")

556

cause = cause.cause

557

}

558

}

559

}

560

```