0
# Error Handling
1
2
This document covers the comprehensive exception system in Koin Core, helping you understand and handle dependency injection failures effectively.
3
4
## Overview
5
6
Koin provides specific exception types for different failure scenarios, enabling precise error handling and debugging. The exception system covers:
7
- **Resolution failures** - When dependencies cannot be found or created
8
- **Scope failures** - When scope operations fail or scopes are misused
9
- **Configuration failures** - When module or application setup goes wrong
10
- **Parameter failures** - When parameter passing or resolution fails
11
12
Understanding these exceptions helps you build robust applications with proper error recovery and meaningful error messages.
13
14
## Exception Hierarchy
15
16
All Koin exceptions extend from standard Kotlin/Java `Exception` class:
17
18
```kotlin { .api }
19
// Base class (standard Kotlin)
20
open class Exception : Throwable
21
22
// Koin-specific exceptions (all in org.koin.core.error package)
23
class NoDefinitionFoundException(msg: String) : Exception(msg)
24
class InstanceCreationException(msg: String, parent: Exception) : Exception(msg, parent)
25
class ClosedScopeException(msg: String) : Exception(msg)
26
// ... and others
27
```
28
29
## Resolution Failures
30
31
### NoDefinitionFoundException
32
33
Thrown when a requested dependency cannot be found in any loaded module.
34
35
```kotlin { .api }
36
class NoDefinitionFoundException(msg: String) : Exception(msg)
37
```
38
39
#### Common Causes
40
41
```kotlin
42
val emptyModule = module {
43
// No UserService definition
44
}
45
46
val koin = koinApplication { modules(emptyModule) }.koin
47
48
try {
49
val service: UserService = koin.get() // NoDefinitionFoundException!
50
} catch (e: NoDefinitionFoundException) {
51
println("Service not found: ${e.message}")
52
// Handle missing dependency
53
}
54
```
55
56
#### Prevention and Handling
57
58
```kotlin
59
// Prevention - ensure all dependencies are defined
60
val completeModule = module {
61
single<UserService> { UserService(get()) }
62
single<UserRepository> { UserRepository() }
63
}
64
65
// Handling - use nullable variants
66
val koin = koinApplication { modules(emptyModule) }.koin
67
68
val optionalService: UserService? = koin.getOrNull()
69
if (optionalService == null) {
70
println("UserService not available, using fallback")
71
// Use fallback implementation
72
}
73
```
74
75
#### Qualified Dependency Issues
76
77
```kotlin
78
val qualifiedModule = module {
79
single<ApiService>(named("v1")) { ApiServiceV1() }
80
}
81
82
val koin = koinApplication { modules(qualifiedModule) }.koin
83
84
try {
85
// Wrong qualifier - will throw NoDefinitionFoundException
86
val service: ApiService = koin.get(named("v2"))
87
} catch (e: NoDefinitionFoundException) {
88
// Fallback to v1 or handle error
89
val fallbackService: ApiService = koin.get(named("v1"))
90
}
91
```
92
93
### InstanceCreationException
94
95
Thrown when a definition exists but instance creation fails due to errors in the definition lambda.
96
97
```kotlin { .api }
98
class InstanceCreationException(msg: String, parent: Exception) : Exception(msg, parent)
99
```
100
101
#### Common Scenarios
102
103
```kotlin
104
val problematicModule = module {
105
single<DatabaseService> {
106
// This will throw during creation
107
throw RuntimeException("Database connection failed")
108
}
109
110
factory<FileService> { params ->
111
val path: String = params.get()
112
if (!File(path).exists()) {
113
throw IllegalArgumentException("File not found: $path")
114
}
115
FileService(path)
116
}
117
}
118
119
val koin = koinApplication { modules(problematicModule) }.koin
120
121
try {
122
val dbService: DatabaseService = koin.get()
123
} catch (e: InstanceCreationException) {
124
println("Failed to create DatabaseService: ${e.message}")
125
println("Root cause: ${e.cause}")
126
// Use mock or fallback implementation
127
}
128
```
129
130
#### Handling Creation Failures
131
132
```kotlin
133
class RobustService : KoinComponent {
134
private val primaryService: DataService? by lazy {
135
try {
136
get<DataService>(named("primary"))
137
} catch (e: InstanceCreationException) {
138
logger.warn("Primary service creation failed", e)
139
null
140
}
141
}
142
143
private val fallbackService: DataService by lazy {
144
get<DataService>(named("fallback"))
145
}
146
147
fun processData(): Result {
148
val service = primaryService ?: fallbackService
149
return service.process()
150
}
151
}
152
```
153
154
## Scope Failures
155
156
### ClosedScopeException
157
158
Thrown when attempting to use a scope that has been closed.
159
160
```kotlin { .api }
161
class ClosedScopeException(msg: String) : Exception(msg)
162
```
163
164
#### Typical Usage Scenarios
165
166
```kotlin
167
val scopedModule = module {
168
scope<UserSession> {
169
scoped<SessionData> { SessionData() }
170
}
171
}
172
173
val koin = koinApplication { modules(scopedModule) }.koin
174
val scope = koin.createScope<UserSession>("session-123")
175
176
// Use scope normally
177
val sessionData: SessionData = scope.get()
178
179
// Close scope
180
scope.close()
181
182
try {
183
// This will throw ClosedScopeException
184
val data: SessionData = scope.get()
185
} catch (e: ClosedScopeException) {
186
println("Attempted to use closed scope: ${e.message}")
187
// Create new scope or handle error
188
val newScope = koin.createScope<UserSession>("session-124")
189
}
190
```
191
192
#### Safe Scope Usage
193
194
```kotlin
195
class ScopeManager : KoinComponent {
196
private var currentScope: Scope? = null
197
198
fun getSessionData(): SessionData? {
199
val scope = currentScope
200
return if (scope != null && !scope.closed) {
201
try {
202
scope.get<SessionData>()
203
} catch (e: ClosedScopeException) {
204
currentScope = null
205
null
206
}
207
} else {
208
null
209
}
210
}
211
212
fun createNewSession(): Scope {
213
currentScope?.takeUnless { it.closed }?.close()
214
currentScope = getKoin().createScope<UserSession>(generateSessionId())
215
return currentScope!!
216
}
217
}
218
```
219
220
### ScopeNotCreatedException
221
222
Thrown when trying to access a scope that doesn't exist.
223
224
```kotlin { .api }
225
class ScopeNotCreatedException(msg: String) : Exception(msg)
226
```
227
228
```kotlin
229
val koin = koinApplication { modules(scopedModule) }.koin
230
231
try {
232
// Scope with this ID doesn't exist
233
val scope = koin.getScope("non-existent-scope")
234
} catch (e: ScopeNotCreatedException) {
235
println("Scope not found: ${e.message}")
236
// Create the scope or handle missing scope
237
val newScope = koin.createScope<UserSession>("non-existent-scope")
238
}
239
240
// Safe alternative
241
val safeScope: Scope? = koin.getScopeOrNull("non-existent-scope")
242
if (safeScope == null) {
243
println("Scope doesn't exist, creating new one")
244
// Handle missing scope gracefully
245
}
246
```
247
248
### ScopeAlreadyCreatedException
249
250
Thrown when attempting to create a scope with an ID that already exists.
251
252
```kotlin { .api }
253
class ScopeAlreadyCreatedException(msg: String) : Exception(msg)
254
```
255
256
```kotlin
257
val koin = koinApplication { modules(scopedModule) }.koin
258
259
// Create first scope
260
val scope1 = koin.createScope<UserSession>("duplicate-id")
261
262
try {
263
// This will throw ScopeAlreadyCreatedException
264
val scope2 = koin.createScope<UserSession>("duplicate-id")
265
} catch (e: ScopeAlreadyCreatedException) {
266
println("Scope already exists: ${e.message}")
267
// Use existing scope or generate new ID
268
val existingScope = koin.getScope("duplicate-id")
269
}
270
271
// Safe alternative - get or create pattern
272
val safeScope = koin.getOrCreateScope<UserSession>("duplicate-id")
273
```
274
275
### NoScopeDefFoundException
276
277
Thrown when trying to create a scope for which no scope definition exists.
278
279
```kotlin { .api }
280
class NoScopeDefFoundException(msg: String) : Exception(msg)
281
```
282
283
```kotlin
284
val moduleWithoutScope = module {
285
single<Service> { Service() }
286
// No scope definition for UndefinedScope
287
}
288
289
val koin = koinApplication { modules(moduleWithoutScope) }.koin
290
291
class UndefinedScope
292
293
try {
294
// No scope definition exists for UndefinedScope
295
val scope = koin.createScope<UndefinedScope>("test")
296
} catch (e: NoScopeDefFoundException) {
297
println("No scope definition found: ${e.message}")
298
// Define the scope or use different approach
299
}
300
```
301
302
### MissingScopeValueException
303
304
Thrown when a scoped instance is expected but not found within a scope.
305
306
```kotlin { .api }
307
class MissingScopeValueException(msg: String) : Exception(msg)
308
```
309
310
## Parameter Failures
311
312
### NoParameterFoundException
313
314
Thrown when a definition expects parameters that are not provided.
315
316
```kotlin { .api }
317
class NoParameterFoundException(msg: String) : Exception(msg)
318
```
319
320
```kotlin
321
val parameterModule = module {
322
factory<DatabaseConnection> { params ->
323
val host: String = params.get() // Expects String parameter
324
val port: Int = params.get() // Expects Int parameter
325
DatabaseConnection(host, port)
326
}
327
}
328
329
val koin = koinApplication { modules(parameterModule) }.koin
330
331
try {
332
// No parameters provided - will throw NoParameterFoundException
333
val connection: DatabaseConnection = koin.get()
334
} catch (e: NoParameterFoundException) {
335
println("Missing parameters: ${e.message}")
336
// Provide required parameters
337
val connection: DatabaseConnection = koin.get {
338
parametersOf("localhost", 5432)
339
}
340
}
341
```
342
343
### DefinitionParameterException
344
345
Thrown when there are parameter type mismatches or parameter access errors.
346
347
```kotlin { .api }
348
class DefinitionParameterException(msg: String) : Exception(msg)
349
```
350
351
```kotlin
352
val typedParameterModule = module {
353
factory<Service> { params ->
354
val config: ServiceConfig = params.get() // Expects ServiceConfig
355
Service(config)
356
}
357
}
358
359
val koin = koinApplication { modules(typedParameterModule) }.koin
360
361
try {
362
// Wrong parameter type - will throw DefinitionParameterException
363
val service: Service = koin.get {
364
parametersOf("wrong-type") // String instead of ServiceConfig
365
}
366
} catch (e: DefinitionParameterException) {
367
println("Parameter type error: ${e.message}")
368
// Provide correct parameter type
369
val service: Service = koin.get {
370
parametersOf(ServiceConfig("correct"))
371
}
372
}
373
```
374
375
## Configuration Failures
376
377
### DefinitionOverrideException
378
379
Thrown when attempting to override a definition when overrides are not allowed.
380
381
```kotlin { .api }
382
class DefinitionOverrideException(msg: String) : Exception(msg)
383
```
384
385
```kotlin
386
val firstModule = module {
387
single<Service> { ServiceImpl1() }
388
}
389
390
val secondModule = module {
391
single<Service> { ServiceImpl2() } // Same type, no qualifier
392
}
393
394
try {
395
val koin = koinApplication {
396
allowOverride(false) // Overrides disabled
397
modules(firstModule, secondModule)
398
}.koin
399
} catch (e: DefinitionOverrideException) {
400
println("Definition override not allowed: ${e.message}")
401
402
// Solutions:
403
// 1. Enable overrides
404
val koinWithOverrides = koinApplication {
405
allowOverride(true)
406
modules(firstModule, secondModule)
407
}.koin
408
409
// 2. Use qualifiers
410
val qualifiedModule = module {
411
single<Service>(named("impl1")) { ServiceImpl1() }
412
single<Service>(named("impl2")) { ServiceImpl2() }
413
}
414
}
415
```
416
417
### KoinApplicationAlreadyStartedException
418
419
Thrown when trying to start a Koin application that's already started (in global context scenarios).
420
421
```kotlin { .api }
422
class KoinApplicationAlreadyStartedException(msg: String) : Exception(msg)
423
```
424
425
### MissingPropertyException
426
427
Thrown when a required property is not found.
428
429
```kotlin { .api }
430
class MissingPropertyException(msg: String) : Exception(msg)
431
```
432
433
```kotlin
434
val propertyModule = module {
435
single<DatabaseConfig> {
436
val host = getProperty<String>("db.host") // Required property
437
val port = getProperty<Int>("db.port")
438
DatabaseConfig(host, port)
439
}
440
}
441
442
val koin = koinApplication {
443
modules(propertyModule)
444
// Properties not set
445
}.koin
446
447
try {
448
val config: DatabaseConfig = koin.get()
449
} catch (e: MissingPropertyException) {
450
println("Missing property: ${e.message}")
451
}
452
453
// Provide properties
454
val koinWithProps = koinApplication {
455
properties(mapOf(
456
"db.host" to "localhost",
457
"db.port" to 5432
458
))
459
modules(propertyModule)
460
}.koin
461
```
462
463
### NoPropertyFileFoundException
464
465
Thrown when a specified property file cannot be found.
466
467
```kotlin { .api }
468
class NoPropertyFileFoundException(msg: String) : Exception(msg)
469
```
470
471
## Error Handling Strategies
472
473
### 1. Graceful Degradation
474
475
```kotlin
476
class ResilientService : KoinComponent {
477
private val primaryCache: Cache? = try {
478
get<Cache>(named("redis"))
479
} catch (e: NoDefinitionFoundException) {
480
logger.warn("Redis cache not available, using in-memory cache")
481
null
482
}
483
484
private val fallbackCache: Cache by lazy { get<Cache>(named("memory")) }
485
486
fun getCache(): Cache = primaryCache ?: fallbackCache
487
}
488
```
489
490
### 2. Retry Mechanisms
491
492
```kotlin
493
class RetryingComponent : KoinComponent {
494
fun getServiceWithRetry(maxAttempts: Int = 3): Service {
495
repeat(maxAttempts) { attempt ->
496
try {
497
return get<Service>()
498
} catch (e: InstanceCreationException) {
499
if (attempt == maxAttempts - 1) throw e
500
logger.warn("Service creation failed (attempt ${attempt + 1}), retrying...")
501
Thread.sleep(1000 * (attempt + 1)) // Exponential backoff
502
}
503
}
504
throw IllegalStateException("Should not reach here")
505
}
506
}
507
```
508
509
### 3. Circuit Breaker Pattern
510
511
```kotlin
512
class CircuitBreakerComponent : KoinComponent {
513
private var failureCount = 0
514
private var lastFailureTime = 0L
515
private val circuitBreakerTimeout = 30_000L // 30 seconds
516
private val failureThreshold = 3
517
518
fun getServiceSafely(): Service? {
519
if (isCircuitOpen()) {
520
logger.warn("Circuit breaker open, service unavailable")
521
return null
522
}
523
524
return try {
525
val service: Service = get()
526
resetCircuitBreaker()
527
service
528
} catch (e: Exception) {
529
recordFailure()
530
logger.error("Service access failed", e)
531
null
532
}
533
}
534
535
private fun isCircuitOpen(): Boolean {
536
return failureCount >= failureThreshold &&
537
(System.currentTimeMillis() - lastFailureTime) < circuitBreakerTimeout
538
}
539
540
private fun recordFailure() {
541
failureCount++
542
lastFailureTime = System.currentTimeMillis()
543
}
544
545
private fun resetCircuitBreaker() {
546
failureCount = 0
547
lastFailureTime = 0L
548
}
549
}
550
```
551
552
### 4. Error Context Collection
553
554
```kotlin
555
class DiagnosticHelper : KoinComponent {
556
fun diagnoseInjectionFailure(type: KClass<*>, qualifier: Qualifier? = null): String {
557
val koin = getKoin()
558
559
return buildString {
560
appendLine("Dependency Injection Diagnostic Report")
561
appendLine("Type: ${type.qualifiedName}")
562
appendLine("Qualifier: ${qualifier?.value ?: "None"}")
563
appendLine()
564
565
// Check if any modules are loaded
566
val moduleCount = koin.instanceRegistry.size()
567
appendLine("Loaded definitions: $moduleCount")
568
569
if (moduleCount == 0) {
570
appendLine("❌ No modules loaded! Check your application setup.")
571
return@buildString
572
}
573
574
// Check for similar types
575
appendLine("Available similar types:")
576
// Implementation would scan registry for similar types
577
578
// Check for scope issues
579
if (qualifier != null) {
580
appendLine("Checking qualified definitions...")
581
// Implementation would check for qualifier matches
582
}
583
}
584
}
585
}
586
```
587
588
## Best Practices
589
590
### 1. Use Nullable Variants for Optional Dependencies
591
592
```kotlin
593
// Good - graceful handling of optional dependencies
594
val optionalService: OptionalService? = koin.getOrNull()
595
596
// Avoid - throwing exceptions for optional features
597
try {
598
val service: OptionalService = koin.get()
599
} catch (e: NoDefinitionFoundException) {
600
// Optional service not available
601
}
602
```
603
604
### 2. Validate Critical Dependencies Early
605
606
```kotlin
607
class ApplicationStartup : KoinComponent {
608
fun validateCriticalDependencies() {
609
val criticalServices = listOf(
610
DatabaseService::class,
611
ConfigService::class,
612
SecurityService::class
613
)
614
615
criticalServices.forEach { serviceClass ->
616
try {
617
getKoin().get(serviceClass, null, null)
618
logger.info("✅ ${serviceClass.simpleName} available")
619
} catch (e: NoDefinitionFoundException) {
620
logger.error("❌ Critical service ${serviceClass.simpleName} missing")
621
throw IllegalStateException("Application cannot start without ${serviceClass.simpleName}", e)
622
}
623
}
624
}
625
}
626
```
627
628
### 3. Scope Lifecycle Management
629
630
```kotlin
631
class SafeScopeComponent : KoinScopeComponent {
632
override val scope: Scope by lazy { createScope() }
633
634
fun cleanup() {
635
try {
636
if (!scope.closed) {
637
scope.close()
638
}
639
} catch (e: ClosedScopeException) {
640
// Scope already closed, ignore
641
logger.debug("Scope already closed during cleanup")
642
}
643
}
644
}
645
```
646
647
### 4. Comprehensive Error Logging
648
649
```kotlin
650
class ErrorLoggingComponent : KoinComponent {
651
fun safeGet<T : Any>(
652
type: KClass<T>,
653
qualifier: Qualifier? = null,
654
parameters: ParametersDefinition? = null
655
): T? {
656
return try {
657
getKoin().get(type, qualifier, parameters)
658
} catch (e: NoDefinitionFoundException) {
659
logger.warn("Definition not found for ${type.simpleName} with qualifier $qualifier")
660
null
661
} catch (e: InstanceCreationException) {
662
logger.error("Failed to create instance of ${type.simpleName}", e)
663
null
664
} catch (e: Exception) {
665
logger.error("Unexpected error resolving ${type.simpleName}", e)
666
null
667
}
668
}
669
}
670
```
671
672
Understanding and properly handling Koin exceptions enables you to build robust applications that gracefully handle dependency injection failures and provide meaningful feedback for debugging and monitoring.