I cannot explain the strange effects of the predicate that you observed, but I assume that your predicate is not doing what you expect.
[NSPredicate predicateWithFormat:@"ANY messages.expiration_date == null OR ANY messages.expiration_date > %@", [NSDate date]];
finds all threads that have a message with expiration_date not set OR have a possible different message that is not expired. What you (probably) need instead is
[NSPredicate predicateWithFormat:@"SUBQUERY(messages, $m, $m.expiration_date == null OR $m.expiration_date > %@).@count > 0", [NSDate date]];