Moose Dying With "Invalid version format (version required)"

I'm putting this here in case anyone else sees this or hits a search engine looking for the problem.

This morning I had to restart my iMac thanks to the lovely security hole Apple patched and my default perlbrew was set to version 5.18.1, though I was developing in 5.18.2. I ran a test and immediately had the following mysterious error:

Invalid version format (version required) at ... /5.18.1/Module/Runtime.pm line 386.

The fix is both simple and complicated.

In version 0.014 of Module::Runtime, released on February 06 (almost three weeks ago as I write this), there's an interesting note in the Changes file:

bugfix: in use_module() and use_package_optimistically(), pass a supplied VERSION parameter through for the version check even if it is undef

In other words, internally the code does this:

sub use_package_optimistically($;$) {                                                                               
    my($name, $version) = @_;

    # some code to load the module

    # pass $version even if it's undefined
    $name->VERSION($version) if @_ >= 2;   # XXX Boom!
    return $name;
}

The error message doesn't tell you which module had an undefined version so I had to do some debugging. As it turns out, it was Moose::Object that was upset. Apparently versions 2.1202 and below were impacted (I'm not sure if all of them were impacted).

The simplest way to replicate this problem is to upgrade to the latest Module::Runtime, make sure your version of Moose is 2.1202 or below and run this:

$ perl -MMoose -e 1

The fix for this is simple: upgrade Moose.

But it's not that easy. Imagine you're in a business environment and you have hundreds and thousands of lines of code dependent on Moose. Moose has gotten pretty stable, but let's face it, if your systems are failing now, do you upgrade Moose and all of its dependencies or do you downgrade Module::Runtime, a small module with no dependencies? You need to minimize risk to production systems, so a panicked rush to upgrade Moose is not a good idea. Downgrading Module::Runtime seems like the right thing to do.

But maybe you decide to upgrade Moose anyway? Then you discover that ElasticSearch also fails with the new Module::Runtime, and who knows how many other modules may face this problem?

This is conceptually similar to the fatal warnings problem. If this helped patch a critical security hole, I'd probably be OK with that. However, more than once I've monkey-patched modules that have global negative global effects with no appreciable gain. I just don't see the win on this one.

9 Comments

Makes we wonder if there is a need for some sort of automated reverse dependency testing system...

Just by the version number this makes downgrading Module::Runtime a no brainer.

With a version number of 0.014 it should be nowhere near production stability. I try to avvoid using modules with revision numbers that low.

That is why I always stay a bit behind the curve. I'm still on perl 5.16.3. But I'm happy You guys sort out the bleading edge problems.

The problem came from how prototypes affect context. Up until Moose 2.1203, in some "load another module, possibly with a specific minimum version" logic, Moose was calling usepackageoptimistically incorrectly, passing whatever $version it had along to Module::Runtime. Because of the prototype, a () was being turned into an explicit undef. In Module::Runtime 0.014, this undef value started being checked in situations were it previously wasn't. Moose was always calling the function wrong, but the error was harmless until Module::Runtime changed.

Unfortunately, this does mean that if you update Module::Runtime to 0.014, you need to update Moose, and possibly other things too that had the similar incorrect calling behaviour (possibly copied directly out of the Moose code -- there is at least one MooseX distribution that had a similar issue). However, there is no way for Moose to indicate this problem in its prerequisites. It works just fine with an older Module::Runtime - it's rather the reverse that's true - a new Module::Runtime doesn't depend on Moose, but rather forces an upgrade on Moose!

Because of the higher frequency of such API compatibility issues, in Moose we have a system for checking for problems like this, using the moose-outdated utility. You can run this at any time and you'll find out if other things need to be updated (e.g. MooseX modules) as a result of updating Moose.

As a result of this Module::Runtime issue, I also added a new unit test to the Moose distribution, called t/zzz-moose-outdated.t. Because of its filename, it will (usually) run last, and it does what moose-outdated does -- checks for breakage issues and very noisily tells you about them. Hopefully this will be seen at the end of an install log, so the user knows he needs to perform more updates as a result of updating Moose.

After I wrote this, I decided to go a bit further with it and implement something that we've talked about in the toolchain group for a while: a new field in distribution metadata called "x_breaks" (x for experimental), which lists module/version combinations that are known to be broken if said distribution is installed. So, Module::Runtime could now add this to its META.json:

    "x_breaks" : {
        "Moose" : "2.1202"
    }

And then anything that understands that field (like say, a t/zzz test of its own!) can wave a big red flag to the user: "Hey, you're using Moose at version 2.1202 or earlier - you need to update that now!"

If you use Dist::Zilla, you can use Dist::Zilla::Plugin::Breaks to add this metadata, and Dist::Zilla::Plugin::Test::CheckBreaks to generate a test that checks the data. And in the future, cpan clients could potentially automatically perform the update for you as needed (as sort of a post-requisite requirement).

test (do only long replies get held for moderation?)

To catch more of such problems earlier it would be nice to have (and run) something like:

Test::DependentModules->minimaltocurrent_version()

Test::WithRequiredModules->minimaltocurrent_version()

It would help to set 'x_breaks' announced by ETHER. Also helps to downgrade required versions numbers to avoid dependency 'bleed and bloat'.

There is also already a "conflicts" key in the META.json spec, albeit IIRC with slightly different semantics.

For example, the current version of Type-Tiny conflicts with version 0.001 and below of Types::ReadOnly. I check for any conflicting software in Makefile.PL and emit a loud warning if it is found. (I probably ought to change the code that checks installed versions to use Module::Metadata instead of loading the module.)

Hopefully future versions of CPAN clients will eventually start doing conflicts checking on their own.

Right, the 'conflicts' key in metadata is a bit different -- it cannot indicate things that need to be updated later, but rather indicates things that cannot be at that version number right now (when we are checking prerequisites, before installing).

I did play around a bit with using conflicts data in some releases, but it turned out to be fairly catastrophically wrong. ;)

Just by the version number this makes downgrading Module::Runtime a no brainer.

With a version number of 0.014 it should be nowhere near production stability. I try to avvoid using modules with revision numbers that low.

You're very wrong here -- you cannot assume anything about the production-worthiness of a module from its version number. Many people, Zefram and myself included, simply start numbering their releases at 0.001 and increment linearly on each subsequent release. Some modules are already excellent at 0.001 and need no further changes. Some modules are terrible even after they are numbered 1.0 or beyond.

See also: http://perl.plover.com/yak/12views/samples/notes.html#sl-9

About Ovid

user-pic Freelance Perl/Testing/Agile consultant and trainer. See http://www.allaroundtheworld.fr/ for our services. If you have a problem with Perl, we will solve it for you. And don't forget to buy my book! http://www.amazon.com/Beginning-Perl-Curtis-Poe/dp/1118013840/