Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inside asyncWrite: "Can only add, remove, or create objects in a Realm in a write transaction - call beginWriteTransaction on an RLMRealm instance first." #8745

Open
aehlke opened this issue Feb 21, 2025 · 1 comment

Comments

@aehlke
Copy link

aehlke commented Feb 21, 2025

How frequently does the bug occur?

Sometimes

Description

I get this error despite being inside asyncWrite

See the repro steps for details

Stacktrace & log output

* thread #2562, queue = 'com.apple.root.user-initiated-qos.cooperative', stop reason = signal SIGABRT
    frame #0: 0x0000000197efb720 libsystem_kernel.dylib`__pthread_kill + 8
    frame #1: 0x000000010415bfa8 libsystem_pthread.dylib`pthread_kill + 288
    frame #2: 0x0000000197e40908 libsystem_c.dylib`abort + 128
    frame #3: 0x0000000197eea44c libc++abi.dylib`abort_message + 132
    frame #4: 0x0000000197ed8a40 libc++abi.dylib`demangling_terminate_handler() + 348
    frame #5: 0x0000000197b813e4 libobjc.A.dylib`_objc_terminate() + 156
    frame #6: 0x0000000197ee9710 libc++abi.dylib`std::__terminate(void (*)()) + 16
    frame #7: 0x0000000197eeccdc libc++abi.dylib`__cxxabiv1::failed_throw(__cxxabiv1::__cxa_exception*) + 88
    frame #8: 0x0000000197eecc84 libc++abi.dylib`__cxa_throw + 92
    frame #9: 0x0000000197b76e40 libobjc.A.dylib`objc_exception_throw + 448
  * frame #10: 0x000000010946a590 RealmSwift`RLMVerifyInWriteTransaction(realm=0x0000600002c902c0) at RLMObjectStore.mm:59:9
    frame #11: 0x000000010946ac90 RealmSwift`RLMAddObjectToRealm(object=0x00000001322fd630, realm=0x0000600002c902c0, updatePolicy=RLMUpdatePolicyUpdateChanged) at RLMObjectStore.mm:112:5
    frame #12: 0x00000001088ca730 RealmSwift`Realm.add(object=(Realm.RLMObjectBase = <no summary available>), update=modified) at Realm.swift:554:9
    frame #13: 0x000000010cba7a10 LakeOfFire`closure #3 in ReaderContentProtocol.addHistoryRecord(realm=RealmSwift.Realm @ 0x000000016c382b78, record=0x00000001322fd630) at ReaderContentProtocol.swift:375:23
    frame #14: 0x000000010cba7a3c LakeOfFire`partial apply for closure #3 in ReaderContentProtocol.addHistoryRecord(realmConfiguration:pageURL:) at <compiler-generated>:0
    frame #15: 0x00000001088d0388 RealmSwift`Realm.asyncWrite<()>(actor=0x00006000035a9100, block=0x10cba7a20) at Realm.swift:1485:23
    frame #16: 0x000000010cba648c LakeOfFire`ReaderContentProtocol.addHistoryRecord(realmConfiguration=RealmSwift.Realm.Configuration @ 0x00000003448d2d30, pageURL="https://www3.nhk.or.jp/news/easy/ne2025022112138/ne2025022112138.html") at ReaderContentProtocol.swift:374:29
    frame #17: 0x000000010cb85990 LakeOfFire`closure #1 in static ReaderContentLoader.load(url="https://www3.nhk.or.jp/news/easy/ne2025022112138/ne2025022112138.html", countsAsHistoryVisit=true, persist=true) at ReaderContentLoader.swift:171:51
    frame #18: 0x000000010cb7f79c LakeOfFire`static ReaderContentLoader.load(url="https://www3.nhk.or.jp/news/easy/ne2025022112138/ne2025022112138.html", persist=true, countsAsHistoryVisit=true) at ReaderContentLoader.swift:147:33
    frame #19: 0x000000010cbf02e4 LakeOfFire`static ReaderViewModel.getContent(pageURL="internal://local/load/reader?reader-url=https%3A%2F%2Fwww3%2Enhk%2Eor%2Ejp%2Fnews%2Feasy%2Fne2025022112138%2Fne2025022112138%2Ehtml") at ReaderViewModel.swift:119:337
    frame #20: 0x000000010cb7d064 LakeOfFire`ReaderContent.load(url="internal://local/load/reader?reader-url=https%3A%2F%2Fwww3%2Enhk%2Eor%2Ejp%2Fnews%2Feasy%2Fne2025022112138%2Fne2025022112138%2Ehtml") at ReaderContent.swift:13:45
    frame #21: 0x000000010cb70dc0 LakeOfFire`closure #1 in Reader.onNavigationCommitted(, state=SwiftUIWebView.WebViewState @ 0x000000016fad0218) at Reader.swift:305:41
    frame #22: 0x000000010cb73868 LakeOfFire`partial apply for closure #1 in Reader.onNavigationCommitted(state:) at <compiler-generated>:0
    frame #23: 0x000000010cb5c370 LakeOfFire`closure #1 in NavigationTaskManager.startOnNavigationCommitted(task=0x000000010d190688 async function pointer to partial apply forwarder for closure #1 () async -> () in LakeOfFire.Reader.onNavigationCommitted(state: SwiftUIWebView.WebViewState) -> ()) at Reader.swift:47:27
    frame #24: 0x000000010cb5c7e0 LakeOfFire`partial apply for closure #1 in NavigationTaskManager.startOnNavigationCommitted(task:) at <compiler-generated>:0
* thread #2562, queue = 'com.apple.root.user-initiated-qos.cooperative', stop reason = signal SIGABRT
    frame #0: 0x0000000197efb720 libsystem_kernel.dylib`__pthread_kill + 8
    frame #1: 0x000000010415bfa8 libsystem_pthread.dylib`pthread_kill + 288
    frame #2: 0x0000000197e40908 libsystem_c.dylib`abort + 128
    frame #3: 0x0000000197eea44c libc++abi.dylib`abort_message + 132
    frame #4: 0x0000000197ed8a40 libc++abi.dylib`demangling_terminate_handler() + 348
    frame #5: 0x0000000197b813e4 libobjc.A.dylib`_objc_terminate() + 156
    frame #6: 0x0000000197ee9710 libc++abi.dylib`std::__terminate(void (*)()) + 16
    frame #7: 0x0000000197eeccdc libc++abi.dylib`__cxxabiv1::failed_throw(__cxxabiv1::__cxa_exception*) + 88
    frame #8: 0x0000000197eecc84 libc++abi.dylib`__cxa_throw + 92
    frame #9: 0x0000000197b76e40 libobjc.A.dylib`objc_exception_throw + 448
  * frame #10: 0x000000010946a590 RealmSwift`RLMVerifyInWriteTransaction(realm=0x0000600002c902c0) at RLMObjectStore.mm:59:9
    frame #11: 0x000000010946ac90 RealmSwift`RLMAddObjectToRealm(object=0x00000001322fd630, realm=0x0000600002c902c0, updatePolicy=RLMUpdatePolicyUpdateChanged) at RLMObjectStore.mm:112:5
    frame #12: 0x00000001088ca730 RealmSwift`Realm.add(object=(Realm.RLMObjectBase = <no summary available>), update=modified) at Realm.swift:554:9
    frame #13: 0x000000010cba7a10 LakeOfFire`closure #3 in ReaderContentProtocol.addHistoryRecord(realm=RealmSwift.Realm @ 0x000000016c382b78, record=0x00000001322fd630) at ReaderContentProtocol.swift:375:23
    frame #14: 0x000000010cba7a3c LakeOfFire`partial apply for closure #3 in ReaderContentProtocol.addHistoryRecord(realmConfiguration:pageURL:) at <compiler-generated>:0
    frame #15: 0x00000001088d0388 RealmSwift`Realm.asyncWrite<()>(actor=0x00006000035a9100, block=0x10cba7a20) at Realm.swift:1485:23
    frame #16: 0x000000010cba648c LakeOfFire`ReaderContentProtocol.addHistoryRecord(realmConfiguration=RealmSwift.Realm.Configuration @ 0x00000003448d2d30, pageURL="https://www3.nhk.or.jp/news/easy/ne2025022112138/ne2025022112138.html") at ReaderContentProtocol.swift:374:29
    frame #17: 0x000000010cb85990 LakeOfFire`closure #1 in static ReaderContentLoader.load(url="https://www3.nhk.or.jp/news/easy/ne2025022112138/ne2025022112138.html", countsAsHistoryVisit=true, persist=true) at ReaderContentLoader.swift:171:51
    frame #18: 0x000000010cb7f79c LakeOfFire`static ReaderContentLoader.load(url="https://www3.nhk.or.jp/news/easy/ne2025022112138/ne2025022112138.html", persist=true, countsAsHistoryVisit=true) at ReaderContentLoader.swift:147:33
    frame #19: 0x000000010cbf02e4 LakeOfFire`static ReaderViewModel.getContent(pageURL="internal://local/load/reader?reader-url=https%3A%2F%2Fwww3%2Enhk%2Eor%2Ejp%2Fnews%2Feasy%2Fne2025022112138%2Fne2025022112138%2Ehtml") at ReaderViewModel.swift:119:337
    frame #20: 0x000000010cb7d064 LakeOfFire`ReaderContent.load(url="internal://local/load/reader?reader-url=https%3A%2F%2Fwww3%2Enhk%2Eor%2Ejp%2Fnews%2Feasy%2Fne2025022112138%2Fne2025022112138%2Ehtml") at ReaderContent.swift:13:45
    frame #21: 0x000000010cb70dc0 LakeOfFire`closure #1 in Reader.onNavigationCommitted(, state=SwiftUIWebView.WebViewState @ 0x000000016fad0218) at Reader.swift:305:41
    frame #22: 0x000000010cb73868 LakeOfFire`partial apply for closure #1 in Reader.onNavigationCommitted(state:) at <compiler-generated>:0
    frame #23: 0x000000010cb5c370 LakeOfFire`closure #1 in NavigationTaskManager.startOnNavigationCommitted(task=0x000000010d190688 async function pointer to partial apply forwarder for closure #1 () async -> () in LakeOfFire.Reader.onNavigationCommitted(state: SwiftUIWebView.WebViewState) -> ()) at Reader.swift:47:27
    frame #24: 0x000000010cb5c7e0 LakeOfFire`partial apply for closure #1 in NavigationTaskManager.startOnNavigationCommitted(task:) at <compiler-generated>:0

Can you reproduce the bug?

Sometimes

Reproduction Steps

The error occurs sporadically and across seemingly random parts of my codebase. I've tried to adopt the same patterns of usage and several defensive strategies, but it hasn't eliminated the error. Here is the latest area where it occurred below.

Notes:

  • I use RealmBackgroundActor for all writes to this Realm, and for all notification subscriptions (including for subscribing via Combine publishers)
  • I use the same Realm object for most writes. I do have a few writes where I instantiate a new Realm object from the same configuration instead of reusing the same existing Realm object (is this a problem?) but these do still use RealmBackgroundActor for initializing the Realm and writing to it
  • I tried adding asyncRefresh before all asyncWrite calls. But I still get this error with or without it.
  • I checked other threads at the moment of this error and while there were several other AsyncCommitHelper::main and ExternalCommitHelper::listen active, they all appeared idle / there was no concurrent work being done that I could see

The error occurred in realm.add(record, update: .modified) at the end of this method

   @RealmBackgroundActor
    func addHistoryRecord(realmConfiguration: Realm.Configuration, pageURL: URL) async throws -> HistoryRecord {
        var imageURL: URL?
        let ref = ThreadSafeReference(to: self)
        if let config = realm?.configuration {
            imageURL = try await { @MainActor in
                let realm = try await Realm(configuration: config, actor: MainActor.shared)
                let content = realm.resolve(ref)
                return try await content?.imageURLToDisplay()
            }()
        }
        guard let realm = await RealmBackgroundActor.shared.cachedRealm(for: realmConfiguration) else { fatalError("Can't get realm for addHistoryRecord") }
        if let record = realm.object(ofType: HistoryRecord.self, forPrimaryKey: HistoryRecord.makePrimaryKey(url: pageURL, html: html)) {
            await realm.asyncRefresh()
            try await realm.asyncWrite {
                record.title = title
                record.imageUrl = imageURL
                record.isFromClipboard = isFromClipboard
                record.rssContainsFullContent = rssContainsFullContent
                if rssContainsFullContent {
                    record.content = content
                }
                record.voiceFrameUrl = voiceFrameUrl
                for audioURL in voiceAudioURLs {
                    if !record.voiceAudioURLs.contains(audioURL) {
                        record.voiceAudioURLs.append(audioURL)
                    }
                }
                record.injectEntryImageIntoHeader = injectEntryImageIntoHeader
                record.publicationDate = publicationDate
//                record.isReaderModeByDefault = isReaderModeByDefault
                record.displayPublicationDate = displayPublicationDate
                record.lastVisitedAt = Date()
                record.isDeleted = false
                if objectSchema.objectClass == Bookmark.self, let bookmark = self as? Bookmark {
                    record.configureBookmark(bookmark)
                }
                record.modifiedAt = Date()
            }
            return record
        } else {
            let record = HistoryRecord()
            record.url = pageURL
            record.title = title
            record.imageUrl = imageURL
            record.rssContainsFullContent = rssContainsFullContent
            if rssContainsFullContent {
                record.content = content
            }
            record.voiceFrameUrl = voiceFrameUrl
            record.voiceAudioURLs.append(objectsIn: voiceAudioURLs)
            record.publicationDate = publicationDate
            record.displayPublicationDate = displayPublicationDate
            record.isFromClipboard = isFromClipboard
            record.isReaderModeByDefault = isReaderModeByDefault
            record.isReaderModeAvailable = isReaderModeAvailable
            record.injectEntryImageIntoHeader = injectEntryImageIntoHeader
            record.lastVisitedAt = Date()
            if objectSchema.objectClass == FeedEntry.self || objectSchema.objectClass == Bookmark.self, let bookmark = self as? Bookmark {
                record.configureBookmark(bookmark)
            }
            record.updateCompoundKey()
            await realm.asyncRefresh()
            try await realm.asyncWrite {
                realm.add(record, update: .modified) // <--- CRASH HERE!
            }
            return record
        }
    }

Version

10.53.0

What Atlas Services are you using?

Local Database only

Are you using encryption?

No

Platform OS and version(s)

macOS 15.2, iOS 18.2

Build environment

Xcode version: 16.2
Dependency manager and version: SPM

@aehlke
Copy link
Author

aehlke commented Feb 21, 2025

I just got this fatal error again in the same place... This is with a debug build, btw. I've had this issue for years sporadically and it's the only remaining critical issue I'm having with RealmSwift so any help is highly appreciated.

I did not get the same crash on the 3rd attempt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant