“Operation canceled” Realm Error Domain=io.realm.unknown Code=89

I’m just trying to access a global Realm file which is on my Realm Cloud (I am NOT on the beta). I get the “Operation canceled” Realm Error Domain=io.realm.unknown Code=89 error when I try to open the Realm.

Realm Studio:

Opening code:

let url = URL(string: "realms://\(MY_INSTANCE_ADDRESS)/common")!
let config = user.configuration(realmURL: url, fullSynchronization: true)

Realm.asyncOpen(configuration: config) { ... }

Authentication code (using PromiseKit):

private func connectToRealm(_ firebaseUser: User) -> Promise<SyncUser> {
    
    // if realm user already logged in, check if it's the right user
    if let user = SyncUser.current {
        guard user.identity == firebaseUser.uid else {
            // it's not the right user. log out and try again.
            user.logOut()
            return connectToRealm(firebaseUser)
        }
        
        // it's the right user.
        return Promise.value(user)
    }

    return firstly {
        // get JWT token with firebase function
        Promise {
            Functions.functions().httpsCallable("myAuthFunction").call(completion: $0.resolve)
        }
        
    }
    .compactMap { result in
        // extract JWT token from response
        return (result?.data as? [String: Any])?["token"] as? String
        
    }
    .then { token in
        // connect to Realm
        Promise {
            SyncUser.logIn(with: SyncCredentials.jwt(token), server:MY_AUTH_URL, onCompletion: $0.resolve)
        }
    }
}

This method is called after logging with Firebase. The auth URL is just https://\(MY_INSTANCE_ADDRESS) .

I suspect it’s a permission problem, in which case: how can I easily let this file be readable by everyone, but not writable? I’m not planning on creating a lot of theses files, I just want to do this once.

This is a crosspost to a StackOverflow question. It’s a good idea to keep questions/answers in one place so we don’t duplicate effort and folks can find answers more quickly. Here’s the link for convenience.

“Operation canceled” Realm Error Domain=io.realm.unknown Code=89

1 Like

Still looking for an answer to this question…

@Jean-Baptiste_Beau

I think the issue is that we can’t duplicate the issue. As a test, I took your code and broke it down a bit. Created a loop that calls writeData 100 times, incrementing id in the loop.

This code created 100 unique objects and printed them to console along the way, the data was both local as well as in our Realm cloud.

class MyObjectType: Object {
   @objc dynamic var id = 0
   @objc dynamic var name_fr = ""
   @objc dynamic var name_eng = ""
}

static let MY_INSTANCE_ADDRESS = "xxxx"
static let AUTH_URL = URL(string: "https://\(MY_INSTANCE_ADDRESS)")!
static let COMMON_REALM_URL = URL(string: "realms://\(MY_INSTANCE_ADDRESS)/common")!

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    SyncUser.logIn(with: SyncCredentials.anonymous(), server: AUTH_URL) { (user, error) in
        guard error == nil && user != nil else {
            print(error)
            return
        }
    }
}

func writeData(withId: Int) {
    let config = SyncUser.current?.configuration(realmURL: COMMON_REALM_URL, fullSynchronization: true)
    
    let realm = try! Realm(configuration: config!)
    
    try! realm.write {
        let x = MyObjectType()
        x.id = withid
        x.name_fr = "test_fr"
        x.name_eng = "test_eng"
        realm.add(x)
     }

    let objectsResult = realm.objects(MyObjectType.self)
    print(objectsResult)
}

And you see the data in Realm Studio? And you can access it if you’re logged in on a different account?

@Jean-Baptiste_Beau

Correct. As I mentioned, I can view it in Realm cloud (meaning realm studio). That code is actually not far off from the code we use in our app (I actually pulled it from our app).

@Jean-Baptiste_Beau You may want to clear the Cache of Realm Studio and see if that resolves the issue. It should be under one of the drop downs of Realm Studio, as I recall, under Help

I tried clearing Cache, doesn’t help. I’ve uninstalled the app on the device countless times too. As @Jay suggested, I checked the logs on Realm studio and saw that I get the following error: {"type":"https://docs.realm.io/server/troubleshoot/errors#access-denied","title":"The path is invalid or current user has no access.","status":403,"code":614} . At least that’s more specific. I tried deleting the realm and recreating one, so now the Realm on the cloud is empty, to avoid schema mismatch as described here. Any idea?

I’m sure the URL is correct because: after deleting the Realm file on Studio, I got this error: {"type":"https://docs.realm.io/server/troubleshoot/errors#access-denied","title":"The path is invalid or current user has no access.","status":403,"detail":"A non-admin user is not allowed to create realms outside their home folder. UserId: '261b6207e5960677d91fec7a75505c46'. RealmPath: '/common'","code":614}. As soon as I recreated the Realm in Studio, this error turned into the one described above.

Okay I can confirm this is a permission problem. In my code, I’ve logged in with some user account, and I’ve got the same error. Then, I made this particular user an administrator in Realm Studio, and I could now load the Realm, read and write data without error! Which leads to my initial question:

You need to give the user permissions
https://docs.realm.io/sync/using-synced-realms/access-control/path-level-permissions

@Ian_Ward well okay, I added that to my JS script that adds objects to the shared realm. I used the code provided by bigFish24 here.
Code looks like this:

async function main() {
  // login
  var adminUser = Realm.Sync.User.current;
  if (adminUser === undefined) {
    let creds = Realm.Sync.Credentials.usernamePassword(adminUsername, adminPassword);
    adminUser = await Realm.Sync.User.login(auth_address, creds);
  }

  // open realm
  const realm = await Realm.open({
    sync: { user: adminUser, url: common_realm_address },
    schema: [MySchema]
  });

  // write data...

  // grant permissions
  const managementRealm = adminUser.openManagementRealm();

  var permObj;
  managementRealm.write(() => {
    permObj = managementRealm.create("PermissionChange", {
      id: "common_all_read",
      createdAt: new Date(),
      updatedAt: new Date(),
      mayManage: false,
      mayWrite: false,
      mayRead: true,
      userId: "*",
      realmUrl: common_realm_address
    });
  });

  // Listen for `PermissionChange` object to be processed
  managementRealm
    .objects("PermissionChange")
    .filtered("id = $0", permObj.id)
    .addListener((objects, changes) => {
      console.log("Permission Status: " + permObj.statusMessage);
    });

  // close realm
  realm.close();
}

And I get the following error:
UnhandledPromiseRejectionWarning: TypeError: adminUser.openManagementRealm is not a function

I guess there’s no way to grant permissions directly in Realm Studio? And that the code I copied is outdated? What would be the correct way now?

@Jean-Baptiste_Beau The way shown in the docs I linked - https://docs.realm.io/sync/using-synced-realms/access-control/path-level-permissions#granting-permissions

I think my question here is that while @Ian_Ward is suggesting to set up permissions, with the code you used on your SO post, you were logging in with anonymous auth and got the error.

With our app, whether with auth as an admin user or anonymous auth, we can access the Realm. e.g.

let creds = SyncCredentials.usernamePassword(username: "realm-admin", password: "password", register: false)
            SyncUser.logIn(with: creds

or

let creds = SyncCredentials.anonymous()
            SyncUser.logIn(with: creds

produces the same result

So in your case, if we draw a correlation, you have an admin user ‘realm-admin’ and if you login with that, you should be able to access your data. If you switch the auth to anonymous, you should still be able to access that same data.

That doesn’t really address the question directly as it’s unclear what this means

readable by everyone

Does ‘everyone’ mean other defined users? e.g you have 10 pre-defined users who you want to have read access (so there would be 10 users listed in the Realm Studio Users tab) or does ‘everyone’ mean everyone that uses the app and does not have a user account set up?

@Jay It seems we are in the same situation, but then I don’t see why I can’t access the data while you can. The global Realm you are accessing, how did you create it? In Realm studio, from client, from server code?

By “readable by everyone”, I mean by every logged-in users, whether they come from anonymous login or JWT. My app is made in such a way that users with no account set up are automatically logged in anonymously to access the shared data, as I believe you can’t access the data if you don’t have a SyncUser instance. That question was addressed here.

@Ian_Ward If I use the code on the doc you link:

// grant permissions
adminUser
  .applyPermissions({ userId: "*" }, realmPath, "read")
  .then(permissionChange => {
    console.log("Permission applied successfully.");
  })
  .catch(error => {
    console.log("Error while applying permission: ", error);
  });

Then neither of the two log statements is printed, and I still see “User permissions: This realm has no permissions” on Realm Studio.

During development, we frequently delete and re-create our database from code so we actually do very little from Realm Studio - mostly use it to monitor data changes as we are building relationships between objects. So everything we do is from code.

As a test, I deleted all data associated with a little Tasks macOS app we have. I ran the app with authenticated user ‘Jay’ (see my code above) and added a few tasks. I then logged out. I then logged in as an anonymous user (using the above code) and all of the Tasks Jay created were available and visible to the anonymous user. Created tasks, logged out, logged back in as Jay and the Tasks persisted.

I then checked the Realm from another device using Realm Studio and all of the data from both logins was there (e.g. it sync’d to the cloud correctly).

Here’s what our Realm Studio looks like - very similar to yours.

@Jay well that’s the behavior I’m trying to achieve :sweat_smile: but it doesn’t work for me… What is your realm creation code? I’m assuming Jay is an admin user here, but do you create the realm from the client app or from backend code?

I thought I found the solution but no… I thought the problem could be related to JS/Swift mismatches, so I made the following:

  1. Delete the Realm file on Realm Studio.
  2. Log in from my client app (swift) with an admin user, created the Realm at path /common, added an element to it. I can see the new object in Realm Studio.
  3. Log out on the client app and log in with an anonymous user. Tried to open the Realm.

I got the exact same error. Operation canceled on the client and "The path is invalid or current user has no access.","status":403,"code":614 on Realm Studio logs.

Well, the Realm is instantiated as soon as the user logs in; the Realm objects are are instantiated in the Realm at that time.

Everything is done in the apps code (macOS, Swift).

Sample code with synchronous Realm opening:

    let MY_INSTANCE_ADDRESS = "myinstance.cloud.realm.io"
    let AUTH_URL = URL(string: "https://\(MY_INSTANCE_ADDRESS)")!
    let COMMON_REALM_URL = URL(string: "realms://\(MY_INSTANCE_ADDRESS)/common")!

    let adminCreds = SyncCredentials.usernamePassword(username: "admin", password: "password")
    
    SyncUser.logIn(with: adminCreds, server: AUTH_URL) { (user, error) in
        guard error == nil && user != nil else { return print(error) }
        print("logged in")
    
        let config = SyncUser.current?.configuration(realmURL: COMMON_REALM_URL, fullSynchronization: true)

        let realm = try! Realm(configuration: config!)
        let x = MyObject(id: 2020, name_fr: "test_fr", name_eng: "test_eng")
        try! realm.write { realm.add(x) }
        
        SyncUser.current?.logOut()
        
        SyncUser.logIn(with: SyncCredentials.anonymous(), server: AUTH_URL) { (user, error) in
            guard error == nil && user != nil else { return print(error) }

            let config = SyncUser.current?.configuration(realmURL: COMMON_REALM_URL, fullSynchronization: true)

            let realm = try! Realm(configuration: config!)
            let x = MyObject(id: 2021, name_fr: "test_fr", name_eng: "test_eng")
            try! realm.write { realm.add(x) }
        }
    }

Result: when opening the Realm synchronously, I don’t get the Operation canceled error on the client but I still get the permission error on Realm Studio. I also don’t see any of the two objects in Realm Studio. I see “This Realm has no classes defined”.