russhwolf / multiplatform-settings

A Kotlin Multiplatform library for saving simple key-value data
Apache License 2.0
1.7k stars 68 forks source link

NoSuchElementException when storing an empty list of serialized items twice in a row #217

Closed geworfener closed 2 hours ago

geworfener commented 4 days ago

Hi,

thanks for your great library.

I get the following exception (at least on Android)

java.util.NoSuchElementException: ArrayDeque is empty.
    at kotlin.collections.ArrayDeque.removeLast(ArrayDeque.kt:165)
    at com.russhwolf.settings.serialization.SettingsEncoder.endStructure(SerializationInternals.kt:62)
    at kotlinx.serialization.internal.CollectionLikeSerializer.serialize(CollectionSerializers.kt:286)
    at com.russhwolf.settings.serialization.SettingsSerializationDelegate.setValue(SerializationDelegates.kt:129)
    at com.example.settingsnosuchelementissue.PlantStorage.set_plants(PlantStorage.kt:22)
    at com.example.settingsnosuchelementissue.PlantStorage.replacePlants(PlantStorage.kt:36)
    at com.example.settingsnosuchelementissue.android.MainActivity.onStart(MainActivity.kt:36)
    at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1582)
    at android.app.Activity.performStart(Activity.java:9008)
    at android.app.ActivityThread.handleStartActivity(ActivityThread.java:4198)
    at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:225)
    at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:205)
    at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:177)
    at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:98)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2685)
    at android.os.Handler.dispatchMessage(Handler.java:106)
    at android.os.Looper.loopOnce(Looper.java:230)
    at android.os.Looper.loop(Looper.java:319)
    at android.app.ActivityThread.main(ActivityThread.java:8919)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:578)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)

when storing an empty list of serialized items twice in a row.

The items field is defined in common code like so:

@OptIn(ExperimentalSettingsApi::class, ExperimentalSerializationApi::class)
private var _myItems: List<MyItemDto> by settings.serializedValue(
    ListSerializer(MyItemDto.serializer()),
    KEY_MY_ITEMS,
    emptyList<MyItemDto>(),
)

@Serializable
data class MyItemDto(
    @SerialName("name")
    val name: String,
    @SerialName("id")
    val id: String,
)

Using

multiplatform-settings = "1.2.0"
kotlinx-serialization = "1.7.3"
settings = { module = "com.russhwolf:multiplatform-settings", version.ref = "multiplatform-settings" }
settings-serialization = { module = "com.russhwolf:multiplatform-settings-serialization", version.ref = "multiplatform-settings" }
settings-no-arg = { module = "com.russhwolf:multiplatform-settings-no-arg", version.ref = "multiplatform-settings" }

I created a reproducer repo here: https://github.com/geworfener/SettingsNoSuchElementIssue

russhwolf commented 3 days ago

Thanks for the reproducer. Seems like there's some issues with top-level lists that I hadn't come across. I'll do some digging and see if I can figure out what's going on.

russhwolf commented 3 days ago

Oops I misclicked on the close button.

I have a fix for this which I will include in the next release.

geworfener commented 2 days ago

Cool, thanks.

russhwolf commented 2 hours ago

Now released in 1.3.0