realm-kotlin
                                
                                 realm-kotlin copied to clipboard
                                
                                    realm-kotlin copied to clipboard
                            
                            
                            
                        Cannot modify managed objects outside of a write transaction
Your documentation says the following - "Collections are mutable and are backed by their corresponding built-in Kotlin classes. You can add and remove elements in a collection within a write transaction." But that's not true.
When I run the following code I get "llegalStateException: Cannot modify managed objects outside of a write transaction":
class Board : RealmObject {
    @PrimaryKey
    var id: String = ""
    var name: String = ""
    var flashCards: RealmList<FlashCard> = realmListOf()
}
class FlashCard : RealmObject {
    var id: String = ""
    var frontText: String = ""
    var backText: String = ""
}
val board = realm.query<Board>("id == $0", boardId).first().find()
realm.write {
    board.flashCards.add(flashCard)
}
Maybe I'm doing something wrong?
Hi @SanEffect. I see that the error message is a bit unclear. The issue is that you are trying to update a frozen object. When querying the realm you get objects tied to the version at the point when the query is executed. The write-block executes on the most recent version of the realm, and in this version your board could potentially have been deleted. To update board you must obtain a reference of the object in the context of the write transaction with findLatest(board). If board has been deleted this will return null.
realm.write {
    findLatest(board)?.flashCards.add(flashCard)   
}
Alternatively you can also query for live objects inside the write-block
realm.write { // this : MutableRealm
    val board = this.query<Board>("id == $0", boardId).first().find()
    board.flashCards.add(flashCard)
}
Hi @rorbech. Thanks to your explanation I realized what I was doing wrong. Previously I tried this approach:
realm.write { // this : MutableRealm
    val board = this.query<Board>("id == $0", boardId).first().find()
    board.flashCards.add(flashCard)
}
But my mistake was that I specified not "this.query()", but "realm.query()" (inside a write transaction). Thank you very much for the detailed explanation! Now adding new elements to the RealmList works perfectly!
@rorbech I think it might be worth keeping this open to track updating the error message to be something that guides the user to a fix.
I'm also getting this same issue in my Compose Multiplatform project, but can't figure out how to fix it. It only happens while I'm updating a RealmList field, with all other fields it works fine.
override suspend fun addQuizToHistory(
        completedQuiz: Quiz,
        earnedPoints: Int
    ): RequestState<Boolean> {
        return if (mongoUser != null) {
            try {
                realm.write {
                    val currentUser = this.query<QuizUser>(
                        query = "ownerId == $0", mongoUser!!.id
                    ).first().find()
                    if (currentUser != null) {
                        currentUser.history.add(
                            QuizHistory().apply {
                                quiz = completedQuiz
                                points = earnedPoints
                                timestamp = Clock.System.now().toEpochMilliseconds()
                            }
                        )
                        RequestState.Success(data = true)
                    } else {
                        RequestState.Error(message = USER_NOT_FOUND)
                    }
                }
            } catch (e: Exception) {
                RequestState.Error(message = e.message.toString())
            }
        } else {
            RequestState.Error(MONGO_USER_NULL_MESSAGE)
        }
    }
Error: Cannot import an outdated object. Use findLatest(object) to find an
@rorbech I've even tried this approach, but still the same issue:
override suspend fun addQuizToHistory(
        completedQuiz: Quiz,
        earnedPoints: Int
    ): RequestState<Boolean> {
        return if (mongoUser != null) {
            try {
                realm.write {
                    val currentUser = this.query<QuizUser>(
                        query = "ownerId == $0", mongoUser!!.id
                    ).first().find()
                    findLatest(currentUser!!)?.history?.add(
                        QuizHistory().apply {
                            quiz = completedQuiz
                            points = earnedPoints
                            timestamp = Clock.System.now().toEpochMilliseconds()
                        }
                    )
                    RequestState.Success(data = true)
                }
            } catch (e: Exception) {
                RequestState.Error(message = e.message.toString())
            }
        } else {
            RequestState.Error(MONGO_USER_NULL_MESSAGE)
        }
    }
Hi @stevdza-san. I am not really certain what is triggering your issue, but if the completedQuiz is an object that is already in the realm, then you need to obtain a reference to the most recent version of it with findLatest(completeQuiz).
Hi @rorbech. I assume you meant to refer to stevdza-san :)
Hi @rorbech. I assume you meant to refer to stevdza-san :)
Yep, sorry.
@rorbech Hey, thanks for the response. I've managed to fix it somehow. Here's my new function implementation, just in case someone might find it helpful:
override suspend fun addQuizToHistory(
        id: ObjectId,
        earnedPoints: Int,
        successRate: Int,
        total: Int,
        correct: Int
    ): RequestState<Unit> {
        return if (mongoUser != null) {
            try {
                realm.write {
                    val currentUser = this.query<QuizUser>(
                        query = "ownerId == $0", mongoUser!!.id
                    ).first().find()
                    if (currentUser != null) {
                        if (currentUser.history.find { it.quizId == id } == null) {
                            currentUser.apply {
                                history.add(
                                    QuizHistory().apply {
                                        quizId = id
                                        points = earnedPoints
                                        success = successRate
                                        totalQuestions = total
                                        correctAnswers = correct
                                        timestamp = Clock.System.now().toEpochMilliseconds()
                                    }
                                )
                            }
                            RequestState.Success(data = Unit)
                        } else {
                            RequestState.Error(QUIZ_ALREADY_COMPLETED)
                        }
                    } else {
                        RequestState.Error(USER_NOT_FOUND)
                    }
                }
            } catch (e: Exception) {
                RequestState.Error(message = e.message.toString())
            }
        } else {
            RequestState.Error(MONGO_USER_NULL)
        }
    }