in reply to calling a sub from an if statement

It isn't that %msgs is out of scope (that would generate an error). It is that sub msg is being called the first time before %msgs has been initialized.

You could fix that with:

my %msgs; BEGIN { %msgs= (400=>"Bad Request",...); }