Handling errors#
Every I/O operation can fail. The filesystem can be full, the
permissions can be wrong, the file can be missing, the network
mount can vanish mid-read. Perl does not raise exceptions for
these by default — it returns a false value and sets $!. The
programmer’s job is to notice.
The or die idiom#
open my $fh, "<", $path or die "open $path: $!";
The form has three loadbearing parts:
or, not||. Precedence matters:open(...) || diewould group the||against the full argument list ofopen. Usingorbinds lowest of all the operators involved and always does what it reads as.The filename in the message.
"$!"alone says “Permission denied” with no context."open $path: $!"says what file, in what step. When logs are all you have, context is everything.$!, not$@.$!is the system error —errnowith a human-readable face.$@is the exception from the most recenteval. Mixing them up at 3am has happened to everyone.
Every open, every close, every print, every read, every
binmode in production code has a check. There is no “it
obviously cannot fail here”. The one time it does, you will want
the diagnostic.
Read $! immediately or not at all#
$! reflects the most recent system call. A naive sequence:
open my $fh, "<", $path;
my $line = <$fh>; # might also set $!
warn "something failed: $!" unless $line;
If the open failed, $! is already overwritten by the time
<$fh> runs on the undefined handle. Check the return value of
each call right where you call it.
Pattern: check the close as carefully as the open#
open my $fh, ">", $path or die "open $path: $!";
print $fh @records or die "write $path: $!";
close $fh or die "close $path: $!";
On a write handle, close is where the kernel actually commits
the buffered data. If the disk is full, print may have
succeeded into memory and close is where you find out. The
close check is not a stylistic flourish; it is the one that
catches the half-written log entry.
For pipes, close additionally waits for the child and sets $?.
See Pipes for the diagnostic wrapper that decomposes
$? into signal and exit-status components.
autodie — errors become exceptions#
For code where every line is a ... or die ... chain, the
autodie pragma is worth the import:
use autodie qw(:all);
open my $fh, "<", $path;
while (my $line = <$fh>) {
# ...
}
close $fh;
With autodie, built-ins like open, close, print, read,
chdir, unlink, and a long list of siblings throw a structured
exception on failure instead of returning false. The caller
catches them with eval { ... } or a block-form try, and the
message names the call and the path automatically.
Two practical notes:
autodieis lexical. It applies only inside the block where it isuse-d. A module you call from anautodieblock does not pick up the pragma.autodiedoes not change the return convention for the success case.openstill returns a true value;$fhis still the handle. You can delete youror diesuffixes and everything else keeps working.
What the Perl exception looks like#
Whether you die manually or let autodie do it, the exception
travels up the stack until something catches it. Catching is
eval plus a check of $@:
my $fh = eval {
open my $h, "<", $path or die "open $path: $!\n";
$h;
};
if ($@) {
warn "could not read $path: $@";
# ... fall back to a default, or return an error, or re-die ...
}
Two habits that save time:
Terminate die strings with
\nwhen you want the raw message. Without a trailing newline, Perl appends" at FILE line N"to thediestring. Sometimes that is what you want; often it is noise.Prefer structured objects for non-trivial errors. A
bless { path => ..., errno => $!, step => "open" }, 'IOError'costs three lines and gives the caller something to introspect instead of a string to regex.
A minimal checklist#
Every open-and-process block should look, structurally, like this:
open my $fh, MODE, $path or die "open $path: $!";
binmode $fh, $layer or die "binmode $path: $!"
if $layer_not_already_in_mode;
# ... do I/O; each op checked in-place ...
close $fh or die "close $path: $!";
If the code deviates — a missing check, a || where an or
belongs, a $@ where $! belongs — that is where the bug is
hiding.
Where to go next#
This chapter covered the everyday open. When you need flag-level
control over the system call itself — for example, O_EXCL to
refuse to clobber, or O_NONBLOCK for a pipe you intend to
select on — reach for sysopen
and its unbuffered companions sysread
and syswrite.