maxrave-dev / maxrave-dev/SimpMusic

[BUG] [Desktop] NullPointerException on track deletion/download when restoring backup with downloadable content from Android

Open
#2,361 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Kotlin
Stars
11.4k
Forks
599
Avg merge
3d 18h
Merged PRs (30d)
7

Description

Duplicate Check
  • I confirm I searched for existing reports and found no duplicates.
Describe the Bug

When restoring a backup created with the "Backup with downloadable content" feature from an Android device onto the Linux Desktop (Compose Multiplatform) client, songs are marked as "Downloaded" but fail to play offline. Attempting to click the "Downloaded" button to remove and re-download the track triggers a java.lang.NullPointerException crash in DownloadUtils.kt.

Steps to Reproduce
  1. Export a "Backup with downloadable content" from the Android version of Simp Music.
  2. Restore this backup file on the Linux Desktop client (CachyOS / Hyprland).
  3. Disconnect internet access and attempt to play a track marked as downloaded (playback fails).
  4. Connect back to the internet and click the "Downloaded" button on any restored track to remove/re-download it.
  5. The application crashes immediately.
Expected Behavior

The app should either map/handle invalid cross-platform storage paths gracefully without throwing a NullPointerException, or allow the user to clear/re-download tracks whose local storage paths do not exist on the current OS.

App Version

1.7.0

Android Version

Android 11 & CachyOS Linux (Hyprland)

Device Model

Samsung A20s & HP Elitebook

Custom ROM
  • I am using a custom ROM
Custom ROM Name (if applicable)

No response

Screenshots (optional)
Image
Log / Error Details

java.lang.NullPointerException: must not be null
at com.simpmusic.media_jvm.b.a.a(DownloadUtils.kt)
at com.maxrave.simpmusic.e.y$b.invokeSuspend(NowPlayingBottomSheetViewModel.kt)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt)
at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWithInternal(DispatchedContinuation.kt)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt)
at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt)
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt)
at kotlinx.coroutines.BuildersKt.launch(Unknown Source)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt)
at kotlinx.coroutines.BuildersKt.launch$default(Unknown Source)
at com.maxrave.simpmusic.e.y.a(NowPlayingBottomSheetViewModel.kt)
at com.maxrave.simpmusic.ui.a.S.a(ModalBottomSheet.kt)
at com.maxrave.simpmusic.ui.a.S.c(ModalBottomSheet.kt)
at androidx.compose.b.r.c(Clickable.kt)
at androidx.compose.b.r.a(Clickable.kt)
at androidx.compose.ui.j.d.h.a(HitPathTracker.kt)
at androidx.compose.ui.j.d.h.a(HitPathTracker.kt)
at androidx.compose.ui.j.d.h.a(HitPathTracker.kt)
at androidx.compose.ui.j.d.h.a(HitPathTracker.kt)
at androidx.compose.ui.j.d.h.a(HitPathTracker.kt)
at androidx.compose.ui.j.d.i.a(HitPathTracker.kt)
at androidx.compose.ui.j.d.e.a(HitPathTracker.kt)
at androidx.compose.ui.j.d.D.a(PointerInputEventProcessor.kt)
at androidx.compose.ui.o.aE.a(RootNodeOwner.skiko.kt)
at androidx.compose.ui.r.c.d(CanvasLayersComposeScene.skiko.kt)
at androidx.compose.ui.r.c.a(CanvasLayersComposeScene.skiko.kt)
at androidx.compose.ui.r.a.b(BaseComposeScene.skiko.kt)
at androidx.compose.ui.r.a.a(BaseComposeScene.skiko.kt)
at androidx.compose.ui.r.a$c.a(BaseComposeScene.skiko.kt)
at androidx.compose.ui.r.a$c.invoke(BaseComposeScene.skiko.kt)
at androidx.compose.ui.j.d.S.j(SyntheticEventSender.skiko.kt)
at androidx.compose.ui.j.d.S.a(SyntheticEventSender.skiko.kt)
at androidx.compose.ui.r.k.a(ComposeSceneInputHandler.skiko.kt)
at androidx.compose.ui.r.k.a(ComposeSceneInputHandler.skiko.kt)
at androidx.compose.ui.r.a.a(BaseComposeScene.skiko.kt)
at androidx.compose.ui.r.f.a(ComposeScene.skiko.kt)
at androidx.compose.ui.r.p.b(ComposeSceneMediator.desktop.kt)
at androidx.compose.ui.r.p.a(ComposeSceneMediator.desktop.kt)
at androidx.compose.ui.r.o.b(ComposeSceneMediator.desktop.kt)
at androidx.compose.ui.r.o.a(ComposeSceneMediator.desktop.kt)
at androidx.compose.ui.r.o$l.mouseReleased(ComposeSceneMediator.desktop.kt)
at java.desktop/java.awt.Component.processMouseEvent(Component.java:6621)
at java.desktop/java.awt.Component.processEvent(Component.java:6386)
at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:4996)
at java.desktop/java.awt.Component.dispatchEvent(Component.java:4828)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:775)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:98)
at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:747)
at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:745)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:744)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@40688ce1, Dispatchers.Main.immediate]

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with DownloadUtils.kt and the NowPlayingBottomSheetViewModel.kt call shown in the stack trace, then reproduce the Android backup restoration on the Linux Desktop client. Trace the restored track's local storage path and verify that clicking Downloaded neither crashes nor leaves the track unusable; completion should allow invalid paths to be cleared or tracks to be re-downloaded.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, kotlin, linux
Domain
desktop, mobile
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.