Realm-cocoa: Object type '(null)' is not managed by the Realm when using sorting on OneToOne child property

Created on 26 Jan 2017  路  13Comments  路  Source: realm/realm-cocoa

Goals

Sorting using Results.sorted() on a child property of the current Realm Object. (eg. "shop.name")
Shop is a Realm object and name a String

Expected Results

It works on a clean install of our app; eg. empty Realm db file. It doesn't work if we're upgrading from our production App (using existing Realm db and migration script)

What did you expected to happen?

It does work on clean install but also when upgrading from an older Realm version and Realm Migration script.

Actual Results

We receive the following crash when we upgrade our app:

Terminating app due to uncaught exception 'RLMException', reason: 'Object type '(null)' is not managed by the Realm. If using a custom objectClasses / objectTypes array in your configuration, add (null) to the list of objectClasses / objectTypes.'

translateErrors is called in the following code:

- (RLMResults *)sortedResultsUsingDescriptors:(NSArray<RLMSortDescriptor *> *)properties {
    if (properties.count == 0) {
        return self;
    }
    return translateErrors([&] {
        if (_results.get_mode() == Results::Mode::Empty) {
            return self;
        }

        return [RLMResults resultsWithObjectInfo:*_info results:_results.sort(RLMSortDescriptorFromDescriptors(*_info, properties))];
    });
}

Steps to Reproduce

What are steps we can follow to reproduce this issue?

Version of Realm and Tooling

Realm 2.2.0 and 2.3.0 - Upgrading from 2.1

Xcode version: 8

iOS/OSX version: 8.4

Dependency manager + version: Cocoapods 1.1.1

T-Bug

Most helpful comment

Thanks for the investigation, @weibel. We should be able to reproduce and fix this based on the analysis you've provided.

All 13 comments

Hi @wbervoets! Thanks for reaching out about this. We'll review this and have someone respond with a solution for follow-up questions soon. Cheers! :)

My apologies for the delay, @wbervoets. We're working through a bit of backlog right now. I'm going to assign the issue to one of the other Cocoa engineers.

Bringing attention back to this issue. cc/ @jpsim @bdash

Any update? If you need more information, please let me know

Hi @wbervoets. My apologies for the delay. There's a lot going on right now! Would you be willing to put together a simple sample project that reproduces the issue while we wait? Thanks.

I'm also having this issue. I added some properties to an object (3 Ints), 2 computed variables (which I added to ignoredProperties()), upgraded schemaVersion and now I'm getting this crash when sorting these objects. This works:

realm.objects(Ingredient.self).filter(Ingredient.createFilter(recipeUuid: recipe.uuid))

But this doesn't:

let nameSort = SortDescriptor(keyPath: "itemOpt.name", ascending: true)
let quantitySort = SortDescriptor(keyPath: "quantity", ascending: true)

let sortDescriptors = [nameSort, quantitySort]

realm.objects(Ingredient.self).filter(Ingredient.createFilter(recipeUuid: recipe.uuid)).sorted(by: sortDescriptors)

My specs:

ProductName: Mac OS X
ProductVersion: 10.12.3
BuildVersion: 16D32

/Applications/Xcode.app/Contents/Developer
Xcode 8.2.1
Build version 8C1002

/usr/local/bin/pod
1.1.1
Realm (2.4.2)
RealmSwift (2.4.2)
RealmSwift (~> 2.4)

/bin/bash
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin16)

/usr/local/bin/carthage
0.18.1
(not in use here)

/usr/bin/git
git version 2.10.1 (Apple Git-78)

Edit: This happened in the simulator. Realm platform was not involved (wasn't logged in). After reset contents and settings and relaunch it didn't happen again.

@i-schuetz can you share the models involved? Specifically Ingredient and whatever type itemOpt is? Also, it'd be useful to know if this crash still occurs if you don't add nameSort to your sortDescriptors array. Thanks!

We are experiencing the same issue.
In our latest app update we updated Realm from 2.1.2 to 2.4.2.

We are heavy users of the new keypath sort. One of our sort options will crash with this error.

If people reinstall the app the problem disappears.

Part of the stacktrace

2017-02-16 14:59:57.823 Vivino regular[55655:3891105] *** Terminating app due to uncaught exception 'RLMException', reason: 'Object type '(null)' is not managed by the Realm. If using a custom `objectClasses` / `objectTypes` array in your configuration, add `(null)` to the list of `objectClasses` / `objectTypes`.'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000010e488d4b __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x0000000114e5c21e objc_exception_throw + 48
    2   Realm                               0x0000000110c5574e _ZN13RLMSchemaInfoixEP8NSString + 558
    3   Realm                               0x0000000110c55256 _ZN12RLMClassInfo14linkTargetTypeEm + 422
    4   Realm                               0x0000000110c9b37b _ZN12_GLOBAL__N_132RLMValidatedColumnIndicesForSortER12RLMClassInfoP8NSString + 1739
    5   Realm                               0x0000000110c9a261 _Z32RLMSortDescriptorFromDescriptorsR12RLMClassInfoP7NSArrayIP17RLMSortDescriptorE + 977
    6   Realm                               0x0000000110db0a54 _ZZ44-[RLMResults sortedResultsUsingDescriptors:]ENK4$_12clEv + 180
    7   Realm                               0x0000000110dae476 _ZL15translateErrorsIZ44-[RLMResults sortedResultsUsingDescriptors:]E4$_12EDaOT_P8NSString + 38
    8   Realm                               0x0000000110dae403 -[RLMResults sortedResultsUsingDescriptors:] + 163

@jpsim I can't test what happens because the crash doesn't happen anymore and don't know how to reproduce it. Here are Ingredient and Item

public final class Ingredient: Object {
    public dynamic var uuid: String = ""
    public dynamic var quantity: Float = 0
    public dynamic var fractionNumerator: Int = 0
    public dynamic var fractionDenominator: Int = 1

    dynamic var unitOpt: Unit? = Unit()
    dynamic var itemOpt: Item? = Item()
    dynamic var recipeOpt: Recipe? = Recipe()

    public override static func primaryKey() -> String? {
        return "uuid"
    }

    public var item: Item {
        get {
            return itemOpt ?? Item()
        }
        set(newItem) {
            itemOpt = newItem
        }
    }

    public var recipe: Recipe {
        get {
            return recipeOpt ?? Recipe()
        }
        set(newRecipe) {
            recipeOpt = newRecipe
        }
    }

    public var unit: Unit {
        get {
            return unitOpt ?? Unit()
        }
        set {
            unitOpt = newValue
        }
    }

    public var fraction: Fraction {
        get {
            return Fraction(wholeNumber: 0, numerator: fractionNumerator, denominator: fractionDenominator)
        }
        set {
            fractionNumerator = newValue.numerator
            fractionDenominator = newValue.denominator
        }
    }

    public convenience init(uuid: String, quantity: Float, fraction: Fraction, unit: Unit, item: Item, recipe: Recipe) {
        self.init(uuid: uuid, quantity: quantity, item: item, recipe: recipe)
        self.fraction = fraction
        self.unit = unit
    }

    // TODO remove
    public convenience init(uuid: String, quantity: Float, item: Item, recipe: Recipe) {
        self.init()

        self.uuid = uuid
        self.quantity = quantity
        self.item = item
        self.recipe = recipe
    }

    public override static func ignoredProperties() -> [String] {
        return ["recipe", "item", "unit", "fraction"]
    }

    // ...
}


public class Item: Object, Identifiable {

    public dynamic var uuid: String = ""
    public dynamic var name: String = ""
    dynamic var categoryOpt: Category? = Category()
    public dynamic var fav: Int = 0


    public var category: Category {
        get {
            return categoryOpt ?? Category()
        }
        set(newCategory) {
            categoryOpt = newCategory
        }
    }

    public override static func primaryKey() -> String? {
        return "uuid"
    }

    public override class func indexedProperties() -> [String] {
        return ["name"]
    }

    public override static func ignoredProperties() -> [String] {
        return ["category"]
    }

    public convenience init(uuid: String, name: String, category: Category, fav: Int) {

        self.init()

        self.uuid = uuid
        self.name = name
        self.category = category
        self.fav = fav
    }
    // ...
}

I dug a bit into this. It looks like things start falling apart inside

RLMClassInfo &RLMClassInfo::linkTargetType(size_t index) {
    if (index < m_linkTargets.size() && m_linkTargets[index]) {
        return *m_linkTargets[index];
    }
    if (m_linkTargets.size() <= index) {
        m_linkTargets.resize(index + 1);
    }
    m_linkTargets[index] = &realm->_info[rlmObjectSchema.properties[index].objectClassName];
    return *m_linkTargets[index];
}

There are the correct number of properties on the rlmObjectSchema and they look like they are correctly filled in, except some of them have an objectClassName of nil hence rlmObjectSchema.properties[index].objectClassName breaks when those ones are used as an index.

Digging deeper I found that the function is not called with the correct index. It was called with 9 but the correct property is at index 10 in rlmObjectSchema.properties.

One property was added to the object schema in the problematic update. When I look at the DB in the Realm Browser for macOS that new property is added to the end of the table (i.e. the last column). But in rlmObjectSchema.properties it is found at the index where we have put it in our class header, hence pushing all subsequent indexes one place (i.e. from 9 to 10).

Looking at

NSUInteger RLMClassInfo::tableColumn(RLMProperty *property) const {
    return objectSchema->persisted_properties[property.index].table_column;
}

In objectSchema->persistedProperties the new property looks like it indexes the correct column in Realm (i.e. the last column).

My hypothesis is that the rlmObjectSchema.propertiesare not sorted correctly when properties have been added to the Realm tables. The order of rlmObjectSchema.properties and objectSchema->persisted_properties[property.index].table_column can get out of sync for a given property.

When the app is reinstalled the Realm columns are ordered as in our object schema and all is good.

Thanks for the investigation, @weibel. We should be able to reproduce and fix this based on the analysis you've provided.

If someone needs to fix this before Realm releases an update, here are two things that will fix it

  1. Recreate the Realm file.
  2. Add any new properties to the end of the object schema. In this case the order of the properties matter.

This was released as part of v2.4.3.

Was this page helpful?
0 / 5 - 0 ratings