supabase-community / supabase-kt

A Kotlin Multiplatform Client for Supabase.
https://supabase.com/docs/reference/kotlin/introduction
MIT License
421 stars 38 forks source link

[Bug]: Error downloading from storage using wasm/ktor3 - IOException: Buffer is empty #701

Closed jmacindoe closed 2 months ago

jmacindoe commented 3 months ago

General Info

Version(s)

2.6.0-wasm0

Kotlin Target(s) and their respective versions

Android 13 (JVM 17)

What happened? (include your code)

I'm seeing the following error on 2.6.0-wasm0. The code runs fine on 2.6.0 (non-wasm) and earlier versions of supabase-kt. The error occurs when downloading a file from supabase storage - see the code below. The file is around 80MB if that's relevant.

I get one progress event: DownloadStatus.Progress(totalBytesReceived=4096, contentLength=88770642)

And then this exception comes through in the Flow catch function in the code below.

java.io.IOException: Buffer is empty
    at io.ktor.utils.io.CloseToken.getCause(CloseToken.kt:37)
    at io.ktor.utils.io.ByteChannel.getClosedCause(ByteChannel.kt:53)
    at io.ktor.utils.io.ByteReadChannelOperationsKt.rethrowCloseCauseIfNeeded(ByteReadChannelOperations.kt:467)
    at io.ktor.utils.io.ByteChannel.flush(ByteChannel.kt:84)
    at io.ktor.utils.io.ByteReadChannelOperationsKt.copyTo(ByteReadChannelOperations.kt:138)
    at io.github.jan.supabase.storage.BucketApiImpl$channelDownloadRequest$4.invokeSuspend(BucketApiImpl.kt:177)
    at io.github.jan.supabase.storage.BucketApiImpl$channelDownloadRequest$4.invoke(Unknown Source:8)
    at io.github.jan.supabase.storage.BucketApiImpl$channelDownloadRequest$4.invoke(Unknown Source:4)
    at io.ktor.client.statement.HttpStatement.execute(HttpStatement.kt:47)
    at io.ktor.client.statement.HttpStatement$execute$1.invokeSuspend(Unknown Source:15)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
    at kotlinx.coroutines.EventLoop.processUnconfinedEvent(EventLoop.common.kt:65)
    at kotlinx.coroutines.internal.DispatchedContinuation.resumeWith(DispatchedContinuation.kt:343)
    at io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith(SuspendFunctionGun.kt:146)
    at io.ktor.util.pipeline.SuspendFunctionGun.loop(SuspendFunctionGun.kt:120)
    at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(SuspendFunctionGun.kt:11)
    at io.ktor.util.pipeline.SuspendFunctionGun$continuation$1.resumeWith(SuspendFunctionGun.kt:70)
    ... 10 more
 Caused by: java.io.IOException: Buffer is empty
    at io.ktor.utils.io.CloseToken.<init>(CloseToken.kt:27)
    at io.ktor.utils.io.ByteChannel.cancel(ByteChannel.kt:125)
    at io.ktor.utils.io.ByteWriteChannelOperationsKt.close(ByteWriteChannelOperations.kt:92)
    at io.ktor.utils.io.ByteReadChannelOperationsKt$reader$job$1.invokeSuspend(ByteReadChannelOperations.kt:283)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
    at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:111)
    at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:99)
    at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:811)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:715)
    at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:702)
 Caused by: java.lang.IllegalArgumentException: Buffer is empty
    at io.ktor.utils.io.core.ByteReadPacketExtensions_jvmKt.read(ByteReadPacketExtensions.jvm.kt:38)
    at io.ktor.utils.io.ByteReadChannelOperations_jvmKt.copyTo(ByteReadChannelOperations.jvm.kt:227)
    at io.ktor.utils.io.ByteReadChannelOperations_jvmKt.copyTo$default(ByteReadChannelOperations.jvm.kt:56)
    at io.ktor.util.cio.FileChannelsKt$writeChannel$1.invokeSuspend(FileChannels.kt:104)
    at io.ktor.util.cio.FileChannelsKt$writeChannel$1.invoke(Unknown Source:8)
    at io.ktor.util.cio.FileChannelsKt$writeChannel$1.invoke(Unknown Source:4)
    at io.ktor.utils.io.ByteReadChannelOperationsKt$reader$job$1.invokeSuspend(ByteReadChannelOperations.kt:275)
    ... 8 more

My code:

    private fun downloadFile(bucket: String, storagePath: String, output: File): Flow<UpdateProgress> {
        if (output.exists()) {
            output.delete()
        }
        return supabase.storage.from(bucket)
            .downloadAuthenticatedToAsFlow(storagePath, output)
            .map { status ->
                when (status) {
                    is DownloadStatus.ByteData -> throw IllegalStateException("Unexpected ByteData")
                    is DownloadStatus.Progress -> UpdateProgress.Progress(status.totalBytesReceived.toFloat()/status.contentLength.toFloat())
                    DownloadStatus.Success -> UpdateProgress.Finished
                }
            }
            .catch {  e ->
                emit(UpdateProgress.Error(e))
            }
    }

Steps To Reproduce (optional)

No response

Relevant log output (optional)

No response

jmacindoe commented 3 months ago

To be clear, the error is occurring on Android but when using 2.6.0-wasm. I think the issue is with downloadAuthenticatedToAsFlow because elsewhere in my codebase I use downloadAuthenticatedAsFlow and uploadAsFlow without any issues on 2.6.0-wasm.

jan-tennert commented 3 months ago

That seems to be a bug within Ktor itself. Apparently it's getting fixed in the upcoming RC, so you'll have to use the byte variant, if you want to use the wasm build.

jmacindoe commented 3 months ago

Ok got it. Thanks for the quick reply!

jan-tennert commented 2 months ago

Fixed in the upcoming 2.7.0-beta-1 version, which uses Ktor 3.0.0-rc-1.