Error Handling
25.1 die and warn
use v5.36;
open(my $fh, '<', $file) or die "Cannot open $file: $!\n"; # $! = OS error message
warn "Config missing, using defaults\n"; # prints to STDERR, continues
die "Something failed"; # without "\n", Perl appends " at script.pl line N."
die { code => 404, message => 'Not found' }; # die with any value (e.g., a hashref)
use Carp qw(croak carp confess cluck);
sub divide($x, $y) {
croak "Division by zero" if $y == 0; # reports the error from the caller's perspective
return $x / $y;
}
# confess: die with a full stack trace; cluck: warn with a stack trace25.2 eval Blocks
my $result = eval {
risky_operation();
1; # return true on success
} or do {
my $error = $@ || 'Unknown error';
warn "Operation failed: $error";
};
# Pitfalls: $@ can be clobbered by DESTROY/nested evals. The idiom above (eval { ...; 1 } or do { ... }) is the safe pattern.
local $@; # protect the caller's $@25.3 try/catch/finally (feature 'try')
For older Perls: Try::Tiny (try { } catch { warn $_ } finally { }; note the trailing semicolon, and return returns from the block, not the sub), Syntax::Keyword::Try (the CPAN module the core feature was based on), or Feature::Compat::Try.
use v5.36;
use feature 'try'; # experimental in 5.34–5.38, stable in 5.40
no warnings 'experimental::try'; # needed before 5.40
sub parse_config($path) {
try {
open my $fh, '<', $path or die "Cannot open $path: $!\n";
my $json = do { local $/; <$fh> };
return decode_json($json); # return works inside try (returns from the sub)
}
catch ($e) {
warn "Config error: $e";
return {};
}
finally { # 5.36+
say 'Config parse attempted';
}
}25.4 Exception Objects
CPAN: Exception::Class (declare hierarchies), Throwable (a Moo/Moose role), Mojo::Exception.
package My::Exception {
use v5.36;
sub new($class, %args) { bless { message => $args{message}, code => $args{code} // 500 }, $class }
sub throw($class, %args) { die $class->new(%args) }
sub message($self) { $self->{message} }
sub code($self) { $self->{code} }
}
package My::Exception::NotFound { our @ISA = ('My::Exception') }
use Scalar::Util qw(blessed);
try {
My::Exception::NotFound->throw(message => 'User 42 not found', code => 404);
}
catch ($e) {
if (blessed $e && $e isa My::Exception::NotFound) {
say 'Not found: ', $e->message;
} elsif (blessed $e && $e->isa('My::Exception')) {
say 'App error: ', $e->code;
} else {
die $e; # rethrow unknown errors
}
}25.5 Warnings Control
use warnings FATAL => 'all'; # turn warnings into errors (strict codebases)
no warnings 'uninitialized'; # disable a category lexically
local $SIG{__WARN__} = sub { log_warning(@_) }; # intercept warnings
local $SIG{__DIE__} = sub { ... }; # hook dies (careful: also fires inside eval)