encrypted-datastore icon indicating copy to clipboard operation
encrypted-datastore copied to clipboard

IncompatibleClassChangeError When updating from 1.0 to 1.1 beta2

Open scottyab opened this issue 9 months ago • 7 comments

Thanks for the great library 👍 . I was looking to upgrade Datastore to 1.1.1 and saw there's a breaking change. So updated encrypted-datastore to 1.1 beta 2 but seeing the below error when attempting to read an existing encrypted datastore (created in v1.0)

java.lang.IncompatibleClassChangeError: Class 'com.google.crypto.tink.streamingaead.InputStreamDecrypter' does not implement interface 'okio.BufferedSource' in call to 'java.io.InputStream okio.BufferedSource.inputStream()' (declaration of 'androidx.datastore.preferences.core.PreferencesSerializer' appears in /data/app/~~eTJYw7Wz9DLV5UqAu9poUA==/com.sptc.mdm-MQep1evLpdddK9RvztVxJA==/base.apk!classes33.dex)
  at androidx.datastore.preferences.core.PreferencesSerializer.readFrom(PreferencesSerializer.jvm.kt:46)
  at io.github.osipxd.datastore.encrypted.StreamingAeadEncryptingSerializer.readEncryptedFrom(EncryptingSerializer.kt:90)
  at io.github.osipxd.datastore.encrypted.WrappingEncryptingSerializer.readFrom(EncryptingSerializer.kt:25)
  at androidx.datastore.core.FileReadScope.readData$suspendImpl(FileStorage.kt:169)
  at androidx.datastore.core.FileReadScope.readData(Unknown Source:0)
  at androidx.datastore.core.StorageConnectionKt$readData$2.invokeSuspend(StorageConnection.kt:74)
  at androidx.datastore.core.StorageConnectionKt$readData$2.invoke(Unknown Source:9)
  at androidx.datastore.core.StorageConnectionKt$readData$2.invoke(Unknown Source:13)
  at androidx.datastore.core.FileStorageConnection.readScope(FileStorage.kt:101)
  at androidx.datastore.core.StorageConnectionKt.readData(StorageConnection.kt:74)
  at androidx.datastore.core.DataStoreImpl.readDataFromFileOrDefault(DataStoreImpl.kt:331)
  at androidx.datastore.core.DataStoreImpl.readDataOrHandleCorruption(DataStoreImpl.kt:373)
  at androidx.datastore.core.DataStoreImpl.access$readDataOrHandleCorruption(DataStoreImpl.kt:53)
  at androidx.datastore.core.DataStoreImpl$InitDataStore$doRun$initData$1.invokeSuspend(DataStoreImpl.kt:445)
  at androidx.datastore.core.DataStoreImpl$InitDataStore$doRun$initData$1.invoke(Unknown Source:8)
  at androidx.datastore.core.DataStoreImpl$InitDataStore$doRun$initData$1.invoke(Unknown Source:2)
  at androidx.datastore.core.SingleProcessCoordinator.lock(SingleProcessCoordinator.kt:41)
  at androidx.datastore.core.DataStoreImpl$InitDataStore.doRun(DataStoreImpl.kt:442)
  at androidx.datastore.core.RunOnce.runIfNeeded(DataStoreImpl.kt:505)
  at androidx.datastore.core.DataStoreImpl.readAndInitOrPropagateAndThrowFailure(DataStoreImpl.kt:274)
  at androidx.datastore.core.DataStoreImpl.access$readAndInitOrPropagateAndThrowFailure(DataStoreImpl.kt:53)
  at androidx.datastore.core.DataStoreImpl$readState$2.invokeSuspend(DataStoreImpl.kt:226)
  at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
  at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:108)
  at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:115)
  at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:103)
  at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
  at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:793)
  at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:697)
  at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:684)
  Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@d5a8401, Dispatchers.Main.immediate]

scottyab avatar May 09 '24 08:05 scottyab

Get the same error for a fresh install (no existing encrypted datastores). LMK if there's more info I can provide. Thanks

scottyab avatar May 09 '24 08:05 scottyab

Thank you for the report! I can't reproduce this crash, so could you please provide output of these Gradle tasks?

./gradlew -q :app:dependencies --configuration releaseRuntimeClasspath
./gradlew -q :app:dependencyInsight --configuration releaseRuntimeClasspath --dependency io.github.osipxd:security-crypto-datastore-preferences

* Remember to replace ':app' with your module name

For the first task, it will be enough to provide only the part related to io.github.osipxd:security-crypto-datastore-preferences. Something like this:

+--- io.github.osipxd:security-crypto-datastore-preferences:1.1.1-beta02
|    +--- io.github.osipxd:encrypted-datastore-preferences:1.1.1-beta02
...
|    \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.23 (*)

osipxd avatar May 09 '24 11:05 osipxd

My guess is you've declared different versions for these two dependencies:

implementation("io.github.osipxd:security-crypto-datastore:1.1.1-beta02")
implementation("io.github.osipxd:security-crypto-datastore-preferences:1.0.0")

I'll add versions alignment (#45) to prevent such mistakes in future. Nice catch!

osipxd avatar May 09 '24 12:05 osipxd

I'm only defining dependency for security-crypto-datastore-preferences is that an issue? I thought it would pull in the security-crypto-datastore dependency.

osipxdSecureDatastore = "1.1.1-beta02"

osipxd-secure-datastore-preferences = { group = "io.github.osipxd", name = "security-crypto-datastore-preferences", version.ref = "osipxdSecureDatastore" }

The gradle dependencies outputs

./gradlew -q :app:dependencies --configuration releaseRuntimeClasspath

Produces.

+--- io.github.osipxd:security-crypto-datastore-preferences:1.1.1-beta02
|    |    +--- io.github.osipxd:encrypted-datastore-preferences:1.1.1-beta02
|    |    |    +--- io.github.osipxd:encrypted-datastore:1.1.1-beta02
|    |    |    |    +--- org.jetbrains.kotlin:kotlin-stdlib:1.9.23 (*)
|    |    |    |    +--- androidx.datastore:datastore-core:1.1.1 (*)
|    |    |    |    \--- com.google.crypto.tink:tink-android:1.13.0 (*)
|    |    |    +--- androidx.datastore:datastore-preferences-core:1.1.1 (*)
|    |    |    \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.23 (*)
|    |    +--- io.github.osipxd:security-crypto-datastore:1.1.1-beta02
|    |    |    +--- io.github.osipxd:encrypted-datastore:1.1.1-beta02 (*)
|    |    |    +--- androidx.datastore:datastore:1.1.1 (*)
|    |    |    +--- androidx.security:security-crypto:1.0.0 -> 1.1.0-alpha06 (*)
|    |    |    \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.23 (*)
|    |    +--- androidx.datastore:datastore-preferences:1.1.1 (*)
|    |    \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.23 (*)

./gradlew -q :app:dependencyInsight --configuration releaseRuntimeClasspath --dependency io.github.osipxd:security-crypto-datastore-preferences

Produces...

io.github.osipxd:security-crypto-datastore-preferences:1.1.1-beta02
  Variant releaseVariantReleaseRuntimePublication:
    | Attribute Name                                  | Provided     | Requested    |
    |-------------------------------------------------|--------------|--------------|
    | org.gradle.dependency.bundling                  | external     |              |
    | org.gradle.libraryelements                      | aar          |              |
    | org.gradle.status                               | release      |              |
    | org.gradle.category                             | library      | library      |
    | org.gradle.usage                                | java-runtime | java-runtime |
    | com.android.build.api.attributes.AgpVersionAttr |              | 8.2.2        |
    | com.android.build.api.attributes.BuildTypeAttr  |              | release      |
    | org.gradle.jvm.environment                      |              | android      |
    | org.jetbrains.kotlin.platform.type              |              | androidJvm   |


io.github.osipxd:security-crypto-datastore-preferences:1.1.1-beta02
+--- project :data
|    +--- releaseRuntimeClasspath
|    \--- project :MyModule1
|         \--- releaseRuntimeClasspath
\--- project :MyModule2
     \--- releaseRuntimeClasspath

scottyab avatar May 09 '24 12:05 scottyab

Oh, it's weird then. The only explanation I see is when security-crypto-datastore-preferences version is lower than androidx.datastore:datastore-preferences-core version. It results to that PreferencesSerializer expects Okio types, but encrypted-datastore 1.0.0 doesn't know about such types as they were introduced in datastore 1.1.0.

According to yours dependencies report, datastore has the same version as encrypted-datastore, so I have no idea what goes wrong. The only idea I have is to release a new version with this PR and check if the problem will persist.

osipxd avatar May 09 '24 20:05 osipxd

Agreed it's strange. I'll jump on and test the new release when available. Thanks!

scottyab avatar May 10 '24 15:05 scottyab

@scottyab, just published v1.1.1-beta03

osipxd avatar May 13 '24 14:05 osipxd

@scottyab, just a gentle reminder. Have you been able to check if the issue persists in beta03?

osipxd avatar May 16 '24 21:05 osipxd

Thanks @osipxd moving to beta03 seems to avoid the IncompatibleClassChangeError we were seeing before. Thanks for the update :)

scottyab avatar May 20 '24 14:05 scottyab

Great news! Closing this issue

osipxd avatar May 20 '24 14:05 osipxd