Hunting down a “Dangling reference to an invalid object” error

Occasionally we receive bug reports from customers where their document is unable to be saved because of a validation error. This happens when the Core Data graph has somehow gotten into an inconsistent state. When saving, a managed object context first validates all changes; any failures cancel the save, reporting back a validation error.

Fortunately, we have quite extensive logging of errors in place for Sandvox, so can gather up the customer’s console log and look through it to see if any details can be gleaned. One such error recently contained the string “Dangling reference to an invalid object”. Googling it turns up quite a wide variety of descriptions. It seems to be Core Data’s fallback to “this relationship is messed up”.

So let’s deconstruct the error. First up, NSValidationErrorObject:

We have a TextBoxBody. It has a temporary managed object ID, so is being saved to the persistent store for the first time. All its properties look reasonable to me.

For NSValidationErrorKey, the error has “pagelet”. The value of pagelet is a TextBox, which also has a temporary object ID. Something about the TextBox has been deemed unsuitable for saving this relationship. It’s present under the key NSValidationErrorValue; let’s take a look:

The inverse of the relationship —body — is in place and is connected up to the expected TextBoxBody, so that’s alright.

But those other properties look a little odd; they’re all at their initial values! I know from experience that this happens to managed objects once they’re disassociated from a managed object context by being deleted. Sadly the description of a managed object doesn’t include this, but I think it’s a good bet this TextBox has been marked for deletion. It has a temporary object ID, which suggests one of the following scenarios:

  • The TextBox was inserted and then deleted, without the context saving in between
  • The TextBox was inserted; undo was used to roll back that change, thereby deleting the object, without the context saving
  • A persistent TextBox was deleted, the context saved, that deletion undone and then redone

What’s even stranger though, is that I have a Cascade delete rule in place for the following TextBox relationships:

  • caption
  • introduction
  • titleBox
  • body

caption, introduction and titleBox are all correctly nil (they’re non-optional too), and have no mention in the error. I strongly suspect Core Data has somehow messed up enforcing its delete rule, deleting and clearing out the others, but leaving body untouched. But how?! Our tests here are unable to reproduce it so far.

It’s probably worth noting that the first three, correctly handled, relationships are all inherited from a super-entity. Only body is actually defined on this entity. Is this some weird edge case where Core Data is working with the wrong entity description perhaps?

There’s one final oddity to note; some other components of the error:

Code 1550 is NSManagedObjectValidationError — "generic validation error". Core Data truly does seem to be saying “there’s something wrong with pagelet, not entirely sure what!”. And then bizarrely, "Dangling reference to an invalid object.” is included as a key in the userInfo dictionary.

We can’t reproduce the bug, so what is to be done? Looks like attempting recovery is the best bet:

If the method returns YES, the save can be retried and should hopefully succeed. I figured testing for the "Dangling reference to an invalid object.” key itself is too risky/subject to change, so everything else is inferred from the current state of the objects. It’s also worth iterating through NSDetailedErrorsKey if present, as there might be multiple validation errors to handle.

© Mike Abdullah 2007-2015