Skip to content
VLSI Mentor

Perl Part I · Module 14 · Errors, Warnings, and Debugging

warn and die

These are the two ways a Perl script reports a problem, and the choice between them is the choice between carrying on and stopping. Both are one word long and there is very little to learn about how they work. What is worth learning is the small formatting rule that decides what your message looks like, and the judgement about which one to use — because the common mistake is not a misuse of the functions but reaching for die where the script could have carried on and told you about every bad row instead of just the first. Both points are measured: section two shows the same message printed two ways, and section three gives a concrete rule for the per-row case.

Foundation10 min readPerlPart IDebugging

Part I · Module 14 · Chapter 14.2 · warn and die

1. One Carries On, One Stops

Azvya Education Pvt. Ltd.VLSI Mentor
warn-vs-die.pl
use strict;
use warnings;
use File::Temp qw(tempdir);
use File::Spec;

my $dir = tempdir(CLEANUP => 1);
my %src = (
  "with-warn.pl" => qq{print "1: before\\n";\nwarn "something looks odd\\n";\nprint "2: after\\n";\n},
  "with-die.pl"  => qq{print "1: before\\n";\ndie  "cannot continue\\n";\nprint "2: after\\n";\n},
);

for my $name (sort keys %src) {
    my $p = File::Spec->catfile($dir, $name);
    open my $w, ">", $p or die $!; print $w $src{$name}; close $w;

    my $out = `perl "$p" 2>/dev/null`;       # stdout only
    my $err = `perl "$p" 2>&1 >/dev/null`;   # stderr only
    my $rc  = $? >> 8;
    for ($out, $err) { s/\s+\z//; s/\n/ | /g }
    printf "%-13s exit %-3d stdout: %-22s stderr: %s\n", $name, $rc, ($out || "(none)"), ($err || "(none)");
}
print "\nwarn goes to STDERR and the script continues; die stops it\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
with-die.pl   exit 255 stdout: 1: before              stderr: cannot continue
with-warn.pl  exit 0   stdout: 1: before | 2: after   stderr: something looks odd

warn goes to STDERR and the script continues; die stops it

Two scripts, identical except for the one word. Three differences in the result:

warn let the script finish. 2: after printed and the exit status is 0.

die stopped it. 2: after never printed and the exit status is 255.

Both messages went to standard error, not standard output. That is why the script runs each file twice — once capturing stdout, once capturing stderr — and it is the detail people forget.

That last point has a real consequence. If you redirect a script's output with perl report.pl > out.txt, the warnings and errors are not in out.txt. They went to the terminal, or in a batch job to wherever stderr was pointed, which is often nowhere. A script that reports problems via warn and is run as script.pl > log 2>/dev/null reports nothing at all.

Two practical notes on that:

  • 2>&1 sends stderr into the same place as stdout — perl report.pl > log 2>&1 keeps everything.
  • Keeping them separate is usually better, because then your actual output is clean data and your diagnostics are elsewhere. Chapter 14.8 builds on exactly this.

2. The Trailing Newline Rule

Azvya Education Pvt. Ltd.VLSI Mentor
the-newline-rule.pl
use strict;
use warnings;
use File::Temp qw(tempdir);
use File::Spec;

my $dir = tempdir(CLEANUP => 1);
my %src = (
  "with-nl.pl"    => qq{die "cannot read the report\\n";\n},
  "without-nl.pl" => qq{die "cannot read the report";\n},
);

for my $name (sort keys %src) {
    my $p = File::Spec->catfile($dir, $name);
    open my $w, ">", $p or die $!; print $w $src{$name}; close $w;
    my $err = `perl "$p" 2>&1 >/dev/null`;
    $err =~ s/\Q$p\E/SCRIPT/g;
    $err =~ s/\s+\z//;
    printf "%-15s %s\n", $name, $err;
}
print "\nno trailing newline -> Perl appends ' at SCRIPT line N.'\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
with-nl.pl      cannot read the report
without-nl.pl   cannot read the report at SCRIPT line 1.

no trailing newline -> Perl appends ' at SCRIPT line N.'

The same message, two results:

  • With \n: cannot read the report
  • Without \n: cannot read the report at SCRIPT line 1.

The rule is simple. If your message ends in a newline, Perl prints it exactly as you wrote it. If it does not, Perl appends at FILE line N. and then the newline.

Which you want depends entirely on who is going to read it:

Ending with \n is for a message aimed at a user. "cannot read /proj/setup.rpt: No such file or directory" is a complete, actionable sentence. The line number in your script is noise to somebody who is not going to open your script.

Leaving the \n off is for a message aimed at you. While developing, or for an error that indicates a bug rather than bad input, the file and line are the most useful part.

3. Which One To Use

The mechanics are settled; the judgement is the rest of the chapter.

Use die when you cannot sensibly continue. The input file does not exist. A required argument is missing. The output directory is not writable. In all of these, carrying on means producing something wrong, and Chapter 13.5 measured exactly that — an unchecked open left a script treating a missing file as an empty one.

Use warn when you can continue and the user should know. A row you could not parse, in a file of ten thousand rows. An optional configuration file that was not found, so defaults are in use. A value outside the range you expected but still usable.

The case worth being deliberate about is the per-row one, because it is the most common and the choice really matters:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
while (my $line = <$fh>) {
    my ($pin, $slack) = parse($line) or do {
        warn "line $.: cannot parse, skipping\n";   # warn, not die
        next;
    };
    ...
}

die on the first bad row tells you about one problem and hides the rest. You fix it, re-run, and find the next one — which on a file with forty malformed rows is forty runs. warn plus next tells you about all forty in one pass.

But — and this is the part people leave out — count them and act on the count at the end:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
warn "skipped $skipped of $total rows\n" if $skipped;
die  "too many bad rows ($skipped of $total), refusing to report\n"
    if $total && $skipped / $total > 0.1;

That gives you the best of both. Every problem is reported, and a file that is mostly broken still fails rather than producing a confident report from 60% of the data. Chapter 14.1's point applies directly: a report built from a fraction of the input, with no indication of that, is the dangerous third kind of failure.

Two more things worth knowing:

die can be caught. A die inside eval does not stop the program — it is trapped and the message lands in $@. That is Chapter 14.4, and it is what makes die usable inside a library rather than only at the top level.

warn can be intercepted. local $SIG{__WARN__} = sub { ... }; lets you route warnings into a log file instead of stderr, which is Chapter 14.11's subject. There is a limit worth knowing: a warning issued at compile time happens before any run-time handler is installed, so this catches run-time warnings only.

4. What To Remember

  • warn reports and continues; die stops. Measured: exit 0 with the next line running, against exit 255 without it.
  • Both write to standard error. So script.pl > out.txt does not capture them, and 2>/dev/null throws them away.
  • 2>&1 keeps everything together, though separate streams are usually better.
  • A message ending in \n prints exactly as written. Without one, Perl appends at FILE line N.
  • Use \n for a message about their data, and leave it off for one about your code.
  • die when you cannot sensibly continue; warn when you can and the user should know.
  • For per-row problems, warn plus next so one pass reports all of them — die on the first row hides the other thirty-nine.
  • Then count the skipped rows and act on the count, or a mostly-broken file produces a confident partial report.
  • A die inside eval is caught (Chapter 14.4); warn can be routed with $SIG{__WARN__} (Chapter 14.11), for run-time warnings only.

Chapter 14.3 sorts out the three variables that hold error information. They look interchangeable, they are not, and using the wrong one gives you a confident message about something that happened somewhere else entirely.

Where this fits

Part of the Perl curriculum.