It has long been known that you can't call $parser->parse() while within another $parser->parse() call, e.g. from a parser hook, because of shared internal parser state. This used to cause problems in MediaWiki all the time 10-ish years ago, until we just told everyone to use a fresh parser when they need to parse wikitext (currently this is done via ParserFactory::getInstance()).
However, this knowledge has not arrived in the MessageCache code. It only has one parser instance, and when you call $msg->parse() while within another $msg->parse(), it just returns HTML-escaped wikitext instead of parsing it (since 3a0ed7a04492 in 2011). This rarely happens, but it does happen, and causes extremely confusing bugs.