koin: java.util.NoSuchElementException when calling getOrNull() while creating an instance of an injected class with parameters

Describe the bug Koin crashes with the following exception when resolving an instance with parameters that has a dependant call to getOrNull() (which gets not provided) to create the instance.

java.util.NoSuchElementException: ArrayDeque is empty.
    at kotlin.collections.ArrayDeque.removeFirst(ArrayDeque.kt:145)
    at org.koin.core.scope.Scope.resolveInstance(Scope.kt:213)

To Reproduce

  1. Create a definition that (a) gets parameters from parametersOf() (b) requires a call to getOrNull() to create the instance for this definition
factory { params ->
    MyClass(
        name = params.get(),
        optionalInjectedParameter = getOrNull()
    )
}
  1. Do not provide a definition for the parameter that should be resolved via getOrNull()
  2. Inject it somewhere
val myInjectedClass = get<MyClass> {
    parametersOf("Some parameter")
}

Whats the problem

  • Koin calls Scope.resolveInstance() for MyClass and puts the parameters on the _parameterStack. It then calls Scope.resolveValue() to create an instance of MyClass.
  • While creating the instance Koin does a call to getOrNull() as we defined it to provide the optional parameter if it is available. It calls Scope.resolveValue() again for the optional parameter and ends up calling _parameterStack.clear() and throwDefinitionNotFound() from there. The exception is caught within getOrNull() so it gracefully returns null for the optional parameter from there and the instance of MyClass gets created correctly in Scope.resolveInstance().
  • But afterwards it tries to remove the parameters of MyClass with _parameterStack.removeFirst(). And since the stack was already cleared from within the getOrNull() call it crashes here.

Expected behavior Koin should create the instance correctly and should not fail due to intentional optional parameters. Please note that it can also be the case that we need to call getOrNull() from the init block of the injected class for some reasons and not only provide it as a constructor parameter.

Koin project used and used version (please complete the following information): koin-core version 3.1.2

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 27
  • Comments: 16 (7 by maintainers)

Commits related to this issue

Most upvoted comments

Ok, let see what’s going on 😕 we need to reproduce the problem.

Fixed in 3.1.4 - 43574dcae258627849a76e1832b8095ce35a4560

I have the same problem. After the release, many users reported this exception, but I can’t reproduce it myself

Hi all, also started seeing this issue the first time when we migrated from 3.0.2 to 3.1.2

Fatal Exception: java.util.NoSuchElementException: ArrayDeque is empty.
       at kotlin.collections.ArrayDeque.removeFirst(ArrayDeque.java:145)
       at org.koin.core.scope.Scope.resolveInstance(Scope.java:213)
       at org.koin.core.scope.Scope.get(Scope.java:193)
       at org.koin.core.scope.Scope.getOrNull(Scope.java:161)
       at org.koin.core.scope.Scope.getOrNull$default(Scope.java:144)
       at org.koin.androidx.workmanager.factory.KoinWorkerFactory.createWorker(KoinWorkerFactory.java:47)
       at androidx.work.DelegatingWorkerFactory.createWorker(DelegatingWorkerFactory.java:71)
       at androidx.work.WorkerFactory.createWorkerWithDefaultFallback(WorkerFactory.java:83)

I’ve just run into this issue on Koin 3.1.5. I’m honestly at a loss as to how removeFirstOrNull can throw a NoSuchElementException. I’ve gotten about ~30 crashes in the last week since upgrading from 2.1.6

Fatal Exception: java.util.NoSuchElementException ArrayDeque is empty. kotlin.collections.ArrayDeque.removeFirst (ArrayDeque.kt:145) kotlin.collections.ArrayDeque.removeFirstOrNull (ArrayDeque.java:157) org.koin.core.scope.Scope.resolveInstance (Scope.kt:244) org.koin.core.scope.Scope.get (Scope.kt:204) org.koin.core.scope.Scope.getOrNull (Scope.kt:172)

This bug is still present in latest releases with removeFirstOrNull. This happens when we use koin to get the instance of an injected class across with parameters across multiple threads. In our case, the creation of this instance was done using Dispatchers.default which is backed by a thread pool.

This is a race condition hence why it is hard to reproduce but I bet that it could be done in a small project doing multithreading.

Hi! Is there any update about this issue? I have the same problem 😞 I reproduced the same bug when using getOrNull() and I also was able to reproduce this crash using get() when dependency was created using multiple scopes (when some dependency is not found in the first scope but in some linked scope, parameters stack is also cleaned which causes crash later on)

I was able to work around it but I’m afraid it has a potential to 🔥 later on unexpectedly since it’s a rather subtle bug

Koin project used and used version: Koin core version 3.1.1