Skip to content

[AI][logs] Crash: IllegalStateException – Query result empty, expected non-null Folder (FolderDao/FolderRepository) #162

@Ethran

Description

@Ethran

Error log (truncated for clarity)

java.lang.IllegalStateException: The query result was empty, but expected a single row to return a NON-NULL object of type <com.ethran.notable.data.db.Folder>.
    at com.ethran.notable.data.db.FolderDao_Impl.get$lambda$0(FolderDao_Impl.kt:221)
    at com.ethran.notable.data.db.FolderDao_Impl.get(FolderDao_Impl.kt:171)
    at com.ethran.notable.data.db.FolderRepository.get(Folder.kt:79)
    at com.ethran.notable.ui.components.BreadCrumbKt.getFolderList(BreadCrumb.kt:74)
    at com.ethran.notable.ui.components.BreadCrumbKt.BreadCrumb(BreadCrumb.kt:51)
    at com.ethran.notable.ui.views.HomeViewKt.Library(HomeView.kt:138)

Possible cause
A DAO/repository call expected a non-null Folder but the query returned no rows. The DAO signature returns a non-null Folder, so a missing row results in IllegalStateException. This may happen if a folder ID is stale/missing or was deleted.

Referenced code

  • Folder entity, DAO and repository (DAO returns non-null; repository uses it):
    • Folder.kt (includes DAO and repository; repository get and DAO get):
      • package com.ethran.notable.data.db
        import android.content.Context
        import androidx.lifecycle.LiveData
        import androidx.room.ColumnInfo
        import androidx.room.Dao
        import androidx.room.Entity
        import androidx.room.ForeignKey
        import androidx.room.Insert
        import androidx.room.PrimaryKey
        import androidx.room.Query
        import androidx.room.Update
        import java.util.Date
        import java.util.UUID
        @Entity(
        foreignKeys = [ForeignKey(
        entity = Folder::class,
        parentColumns = arrayOf("id"),
        childColumns = arrayOf("parentFolderId"),
        onDelete = ForeignKey.CASCADE
        )]
        )
        data class Folder(
        @PrimaryKey
        val id: String = UUID.randomUUID().toString(),
        val title: String = "New Folder",
        @ColumnInfo(index = true)
        val parentFolderId: String? = null,
        val createdAt: Date = Date(),
        val updatedAt: Date = Date()
        )
        // DAO
        @Dao
        interface FolderDao {
        @Query("SELECT * FROM folder WHERE parentFolderId IS :folderId")
        fun getChildrenFolders(folderId: String?): LiveData<List<Folder>>
        @Query("SELECT * FROM folder WHERE id IS :folderId")
        fun get(folderId: String): Folder
        @Insert
        fun create(folder: Folder): Long
        @Update
        fun update(folder: Folder)
        @Query("DELETE FROM folder WHERE id=:id")
        fun delete(id: String)
        }
        class FolderRepository(context: Context) {
        var db = AppDatabase.getDatabase(context).folderDao()
        fun create(folder: Folder) {
        db.create(folder)
        }
        fun update(folder: Folder) {
        db.update(folder)
        }
        fun getAllInFolder(folderId: String? = null): LiveData<List<Folder>> {
        return db.getChildrenFolders(folderId)
        }
        fun getParent(folderId: String? = null): String? {
        if (folderId == null)
        return null
        val folder = db.get(folderId)
        return folder.parentFolderId
        }
        fun get(folderId: String): Folder {
        return db.get(folderId)
        }
        fun delete(id: String) {
        db.delete(id)
        }
        }
      • Specific repository call site (FolderRepository.get):
  • BreadCrumb usage path:
    • getFolderList (calls FolderRepository.get and recurses):
      fun getFolderList(context: Context, folderId: String): List<Folder> {
      @Suppress("USELESS_ELVIS")
      val folder = FolderRepository(context).get(folderId) ?: return emptyList()
      val folderList = mutableListOf(folder)
      val parentId = folder.parentFolderId
      if (parentId != null) {
      folderList.addAll(getFolderList(context, parentId))
      }
      return folderList
      }
    • BreadCrumb composable:
      package com.ethran.notable.ui.components
      import android.content.Context
      import androidx.compose.foundation.layout.Arrangement
      import androidx.compose.foundation.layout.Row
      import androidx.compose.foundation.layout.padding
      import androidx.compose.material.Icon
      import androidx.compose.material.Text
      import androidx.compose.material.icons.Icons
      import androidx.compose.material.icons.filled.ChevronRight
      import androidx.compose.runtime.Composable
      import androidx.compose.ui.Alignment
      import androidx.compose.ui.Modifier
      import androidx.compose.ui.platform.LocalContext
      import androidx.compose.ui.text.style.TextDecoration
      import androidx.compose.ui.text.style.TextOverflow
      import androidx.compose.ui.unit.dp
      import androidx.compose.ui.unit.sp
      import com.ethran.notable.R
      import com.ethran.notable.data.db.Folder
      import com.ethran.notable.data.db.FolderRepository
      import com.ethran.notable.ui.noRippleClickable
      @Composable
      fun BreadCrumb(
      modifier: Modifier = Modifier,
      folderId: String? = null,
      fontSize: Int = 20,
      onSelectFolderId: (String?) -> Unit
      ) {
      val context = LocalContext.current
      Row(
      modifier = modifier.padding(horizontal = 8.dp, vertical = 4.dp),
      verticalAlignment = Alignment.CenterVertically,
      horizontalArrangement = Arrangement.spacedBy(4.dp)
      ) {
      Text(
      text = context.getString(R.string.home_view_name),
      fontSize = fontSize.sp,
      textDecoration = TextDecoration.Underline,
      maxLines = 1,
      overflow = TextOverflow.Ellipsis,
      modifier = Modifier
      .padding(end = 2.dp)
      .noRippleClickable { onSelectFolderId(null) }
      )
      // ToolbarButton(iconId = R.drawable.home, onSelect = { onSelectFolderId(null) })
      if (folderId != null) {
      val folders: List<Folder> = getFolderList(context, folderId).reversed()
      folders.forEach { f ->
      Icon(
      imageVector = Icons.Filled.ChevronRight,
      contentDescription = null, // decorative
      modifier = Modifier.padding(horizontal = 2.dp)
      )
      Text(
      text = f.title,
      fontSize = fontSize.sp,
      textDecoration = TextDecoration.Underline,
      maxLines = 1,
      overflow = TextOverflow.Ellipsis,
      modifier = Modifier.noRippleClickable { onSelectFolderId(f.id) }
      )
      }
      }
      }
      }
  • Home screen where BreadCrumb is used:

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions