Koin Core is a pragmatic lightweight dependency injection framework for Kotlin Multiplatform with iOS Simulator ARM64 target support.
—
This document covers the comprehensive exception system in Koin Core, helping you understand and handle dependency injection failures effectively.
Koin provides specific exception types for different failure scenarios, enabling precise error handling and debugging. The exception system covers:
Understanding these exceptions helps you build robust applications with proper error recovery and meaningful error messages.
All Koin exceptions extend from standard Kotlin/Java Exception class:
// Base class (standard Kotlin)
open class Exception : Throwable
// Koin-specific exceptions (all in org.koin.core.error package)
class NoDefinitionFoundException(msg: String) : Exception(msg)
class InstanceCreationException(msg: String, parent: Exception) : Exception(msg, parent)
class ClosedScopeException(msg: String) : Exception(msg)
// ... and othersThrown when a requested dependency cannot be found in any loaded module.
class NoDefinitionFoundException(msg: String) : Exception(msg)val emptyModule = module {
// No UserService definition
}
val koin = koinApplication { modules(emptyModule) }.koin
try {
val service: UserService = koin.get() // NoDefinitionFoundException!
} catch (e: NoDefinitionFoundException) {
println("Service not found: ${e.message}")
// Handle missing dependency
}// Prevention - ensure all dependencies are defined
val completeModule = module {
single<UserService> { UserService(get()) }
single<UserRepository> { UserRepository() }
}
// Handling - use nullable variants
val koin = koinApplication { modules(emptyModule) }.koin
val optionalService: UserService? = koin.getOrNull()
if (optionalService == null) {
println("UserService not available, using fallback")
// Use fallback implementation
}val qualifiedModule = module {
single<ApiService>(named("v1")) { ApiServiceV1() }
}
val koin = koinApplication { modules(qualifiedModule) }.koin
try {
// Wrong qualifier - will throw NoDefinitionFoundException
val service: ApiService = koin.get(named("v2"))
} catch (e: NoDefinitionFoundException) {
// Fallback to v1 or handle error
val fallbackService: ApiService = koin.get(named("v1"))
}Thrown when a definition exists but instance creation fails due to errors in the definition lambda.
class InstanceCreationException(msg: String, parent: Exception) : Exception(msg, parent)val problematicModule = module {
single<DatabaseService> {
// This will throw during creation
throw RuntimeException("Database connection failed")
}
factory<FileService> { params ->
val path: String = params.get()
if (!File(path).exists()) {
throw IllegalArgumentException("File not found: $path")
}
FileService(path)
}
}
val koin = koinApplication { modules(problematicModule) }.koin
try {
val dbService: DatabaseService = koin.get()
} catch (e: InstanceCreationException) {
println("Failed to create DatabaseService: ${e.message}")
println("Root cause: ${e.cause}")
// Use mock or fallback implementation
}class RobustService : KoinComponent {
private val primaryService: DataService? by lazy {
try {
get<DataService>(named("primary"))
} catch (e: InstanceCreationException) {
logger.warn("Primary service creation failed", e)
null
}
}
private val fallbackService: DataService by lazy {
get<DataService>(named("fallback"))
}
fun processData(): Result {
val service = primaryService ?: fallbackService
return service.process()
}
}Thrown when attempting to use a scope that has been closed.
class ClosedScopeException(msg: String) : Exception(msg)val scopedModule = module {
scope<UserSession> {
scoped<SessionData> { SessionData() }
}
}
val koin = koinApplication { modules(scopedModule) }.koin
val scope = koin.createScope<UserSession>("session-123")
// Use scope normally
val sessionData: SessionData = scope.get()
// Close scope
scope.close()
try {
// This will throw ClosedScopeException
val data: SessionData = scope.get()
} catch (e: ClosedScopeException) {
println("Attempted to use closed scope: ${e.message}")
// Create new scope or handle error
val newScope = koin.createScope<UserSession>("session-124")
}class ScopeManager : KoinComponent {
private var currentScope: Scope? = null
fun getSessionData(): SessionData? {
val scope = currentScope
return if (scope != null && !scope.closed) {
try {
scope.get<SessionData>()
} catch (e: ClosedScopeException) {
currentScope = null
null
}
} else {
null
}
}
fun createNewSession(): Scope {
currentScope?.takeUnless { it.closed }?.close()
currentScope = getKoin().createScope<UserSession>(generateSessionId())
return currentScope!!
}
}Thrown when trying to access a scope that doesn't exist.
class ScopeNotCreatedException(msg: String) : Exception(msg)val koin = koinApplication { modules(scopedModule) }.koin
try {
// Scope with this ID doesn't exist
val scope = koin.getScope("non-existent-scope")
} catch (e: ScopeNotCreatedException) {
println("Scope not found: ${e.message}")
// Create the scope or handle missing scope
val newScope = koin.createScope<UserSession>("non-existent-scope")
}
// Safe alternative
val safeScope: Scope? = koin.getScopeOrNull("non-existent-scope")
if (safeScope == null) {
println("Scope doesn't exist, creating new one")
// Handle missing scope gracefully
}Thrown when attempting to create a scope with an ID that already exists.
class ScopeAlreadyCreatedException(msg: String) : Exception(msg)val koin = koinApplication { modules(scopedModule) }.koin
// Create first scope
val scope1 = koin.createScope<UserSession>("duplicate-id")
try {
// This will throw ScopeAlreadyCreatedException
val scope2 = koin.createScope<UserSession>("duplicate-id")
} catch (e: ScopeAlreadyCreatedException) {
println("Scope already exists: ${e.message}")
// Use existing scope or generate new ID
val existingScope = koin.getScope("duplicate-id")
}
// Safe alternative - get or create pattern
val safeScope = koin.getOrCreateScope<UserSession>("duplicate-id")Thrown when trying to create a scope for which no scope definition exists.
class NoScopeDefFoundException(msg: String) : Exception(msg)val moduleWithoutScope = module {
single<Service> { Service() }
// No scope definition for UndefinedScope
}
val koin = koinApplication { modules(moduleWithoutScope) }.koin
class UndefinedScope
try {
// No scope definition exists for UndefinedScope
val scope = koin.createScope<UndefinedScope>("test")
} catch (e: NoScopeDefFoundException) {
println("No scope definition found: ${e.message}")
// Define the scope or use different approach
}Thrown when a scoped instance is expected but not found within a scope.
class MissingScopeValueException(msg: String) : Exception(msg)Thrown when a definition expects parameters that are not provided.
class NoParameterFoundException(msg: String) : Exception(msg)val parameterModule = module {
factory<DatabaseConnection> { params ->
val host: String = params.get() // Expects String parameter
val port: Int = params.get() // Expects Int parameter
DatabaseConnection(host, port)
}
}
val koin = koinApplication { modules(parameterModule) }.koin
try {
// No parameters provided - will throw NoParameterFoundException
val connection: DatabaseConnection = koin.get()
} catch (e: NoParameterFoundException) {
println("Missing parameters: ${e.message}")
// Provide required parameters
val connection: DatabaseConnection = koin.get {
parametersOf("localhost", 5432)
}
}Thrown when there are parameter type mismatches or parameter access errors.
class DefinitionParameterException(msg: String) : Exception(msg)val typedParameterModule = module {
factory<Service> { params ->
val config: ServiceConfig = params.get() // Expects ServiceConfig
Service(config)
}
}
val koin = koinApplication { modules(typedParameterModule) }.koin
try {
// Wrong parameter type - will throw DefinitionParameterException
val service: Service = koin.get {
parametersOf("wrong-type") // String instead of ServiceConfig
}
} catch (e: DefinitionParameterException) {
println("Parameter type error: ${e.message}")
// Provide correct parameter type
val service: Service = koin.get {
parametersOf(ServiceConfig("correct"))
}
}Thrown when attempting to override a definition when overrides are not allowed.
class DefinitionOverrideException(msg: String) : Exception(msg)val firstModule = module {
single<Service> { ServiceImpl1() }
}
val secondModule = module {
single<Service> { ServiceImpl2() } // Same type, no qualifier
}
try {
val koin = koinApplication {
allowOverride(false) // Overrides disabled
modules(firstModule, secondModule)
}.koin
} catch (e: DefinitionOverrideException) {
println("Definition override not allowed: ${e.message}")
// Solutions:
// 1. Enable overrides
val koinWithOverrides = koinApplication {
allowOverride(true)
modules(firstModule, secondModule)
}.koin
// 2. Use qualifiers
val qualifiedModule = module {
single<Service>(named("impl1")) { ServiceImpl1() }
single<Service>(named("impl2")) { ServiceImpl2() }
}
}Thrown when trying to start a Koin application that's already started (in global context scenarios).
class KoinApplicationAlreadyStartedException(msg: String) : Exception(msg)Thrown when a required property is not found.
class MissingPropertyException(msg: String) : Exception(msg)val propertyModule = module {
single<DatabaseConfig> {
val host = getProperty<String>("db.host") // Required property
val port = getProperty<Int>("db.port")
DatabaseConfig(host, port)
}
}
val koin = koinApplication {
modules(propertyModule)
// Properties not set
}.koin
try {
val config: DatabaseConfig = koin.get()
} catch (e: MissingPropertyException) {
println("Missing property: ${e.message}")
}
// Provide properties
val koinWithProps = koinApplication {
properties(mapOf(
"db.host" to "localhost",
"db.port" to 5432
))
modules(propertyModule)
}.koinThrown when a specified property file cannot be found.
class NoPropertyFileFoundException(msg: String) : Exception(msg)class ResilientService : KoinComponent {
private val primaryCache: Cache? = try {
get<Cache>(named("redis"))
} catch (e: NoDefinitionFoundException) {
logger.warn("Redis cache not available, using in-memory cache")
null
}
private val fallbackCache: Cache by lazy { get<Cache>(named("memory")) }
fun getCache(): Cache = primaryCache ?: fallbackCache
}class RetryingComponent : KoinComponent {
fun getServiceWithRetry(maxAttempts: Int = 3): Service {
repeat(maxAttempts) { attempt ->
try {
return get<Service>()
} catch (e: InstanceCreationException) {
if (attempt == maxAttempts - 1) throw e
logger.warn("Service creation failed (attempt ${attempt + 1}), retrying...")
Thread.sleep(1000 * (attempt + 1)) // Exponential backoff
}
}
throw IllegalStateException("Should not reach here")
}
}class CircuitBreakerComponent : KoinComponent {
private var failureCount = 0
private var lastFailureTime = 0L
private val circuitBreakerTimeout = 30_000L // 30 seconds
private val failureThreshold = 3
fun getServiceSafely(): Service? {
if (isCircuitOpen()) {
logger.warn("Circuit breaker open, service unavailable")
return null
}
return try {
val service: Service = get()
resetCircuitBreaker()
service
} catch (e: Exception) {
recordFailure()
logger.error("Service access failed", e)
null
}
}
private fun isCircuitOpen(): Boolean {
return failureCount >= failureThreshold &&
(System.currentTimeMillis() - lastFailureTime) < circuitBreakerTimeout
}
private fun recordFailure() {
failureCount++
lastFailureTime = System.currentTimeMillis()
}
private fun resetCircuitBreaker() {
failureCount = 0
lastFailureTime = 0L
}
}class DiagnosticHelper : KoinComponent {
fun diagnoseInjectionFailure(type: KClass<*>, qualifier: Qualifier? = null): String {
val koin = getKoin()
return buildString {
appendLine("Dependency Injection Diagnostic Report")
appendLine("Type: ${type.qualifiedName}")
appendLine("Qualifier: ${qualifier?.value ?: "None"}")
appendLine()
// Check if any modules are loaded
val moduleCount = koin.instanceRegistry.size()
appendLine("Loaded definitions: $moduleCount")
if (moduleCount == 0) {
appendLine("❌ No modules loaded! Check your application setup.")
return@buildString
}
// Check for similar types
appendLine("Available similar types:")
// Implementation would scan registry for similar types
// Check for scope issues
if (qualifier != null) {
appendLine("Checking qualified definitions...")
// Implementation would check for qualifier matches
}
}
}
}// Good - graceful handling of optional dependencies
val optionalService: OptionalService? = koin.getOrNull()
// Avoid - throwing exceptions for optional features
try {
val service: OptionalService = koin.get()
} catch (e: NoDefinitionFoundException) {
// Optional service not available
}class ApplicationStartup : KoinComponent {
fun validateCriticalDependencies() {
val criticalServices = listOf(
DatabaseService::class,
ConfigService::class,
SecurityService::class
)
criticalServices.forEach { serviceClass ->
try {
getKoin().get(serviceClass, null, null)
logger.info("✅ ${serviceClass.simpleName} available")
} catch (e: NoDefinitionFoundException) {
logger.error("❌ Critical service ${serviceClass.simpleName} missing")
throw IllegalStateException("Application cannot start without ${serviceClass.simpleName}", e)
}
}
}
}class SafeScopeComponent : KoinScopeComponent {
override val scope: Scope by lazy { createScope() }
fun cleanup() {
try {
if (!scope.closed) {
scope.close()
}
} catch (e: ClosedScopeException) {
// Scope already closed, ignore
logger.debug("Scope already closed during cleanup")
}
}
}class ErrorLoggingComponent : KoinComponent {
fun safeGet<T : Any>(
type: KClass<T>,
qualifier: Qualifier? = null,
parameters: ParametersDefinition? = null
): T? {
return try {
getKoin().get(type, qualifier, parameters)
} catch (e: NoDefinitionFoundException) {
logger.warn("Definition not found for ${type.simpleName} with qualifier $qualifier")
null
} catch (e: InstanceCreationException) {
logger.error("Failed to create instance of ${type.simpleName}", e)
null
} catch (e: Exception) {
logger.error("Unexpected error resolving ${type.simpleName}", e)
null
}
}
}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.
Install with Tessl CLI
npx tessl i tessl/maven-io-insert-koin--koin-core-iossimulatorarm64