Skip to content
VLSI Mentor

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

eval { }: Catching a Fatal Error

A die normally stops the script. Wrapping the risky part in eval changes that: the die is trapped, the message lands in $@, and your script carries on and decides what to do. This is how you write code that can recover — retry a read, fall back to a default, skip one bad file out of two hundred. The mechanism takes one sentence to describe. The reason this chapter is longer than that is the idiom everybody writes, which has a measured failure mode: in two of four cases it reports an error when nothing went wrong. You will see exactly why, and the one character that fixes it.

Intermediate15 min readPerlPart IDebugging

Part I · Module 14 · Chapter 14.4 · eval { }: Catching a Fatal Error

1. What eval Does

eval takes a block. It runs it. If anything inside dies, the death stops at the closing brace instead of stopping the program.

Azvya Education Pvt. Ltd.VLSI Mentor
eval-basics.pl
use strict;
use warnings;

print "A: before\n";

eval {
    print "B: inside the eval\n";
    die "something failed\n";
    print "C: never reached\n";
};

my $err = $@;
$err =~ s/\s+\z//;
print "D: eval finished, \$\@ = '$err'\n";
print "E: the script is still running\n";

# and on the success path
eval { print "F: a block that does not die\n" };
print "G: \$\@ is now '", $@, "' -- eval cleared it on success\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
A: before
B: inside the eval
D: eval finished, $@ = 'something failed'
E: the script is still running
F: a block that does not die
G: $@ is now '' -- eval cleared it on success

Four things to notice:

The die did not stop the script. D and E both printed. Without the eval, the script would have ended at the die with exit status 255, as Chapter 14.2 measured.

Statements after the die inside the block were skipped. C never printed. eval does not resume where it left off — it abandons the rest of the block.

The message landed in $@, exactly as given to die.

A successful eval cleared $@ to the empty string. That is the behaviour Chapter 14.3 measured and it is the reason the script copies $@ into $err immediately.

2. The Trailing 1, and Why It Is Not Optional

The idiom you will see everywhere is eval { ... } or do { ... }. It reads nicely: try this, or else handle the problem. It is also wrong as written, and this is worth measuring rather than taking on trust.

The reason is that eval returns the value of the last statement in the block, and or tests that value — not whether anything died.

Azvya Education Pvt. Ltd.VLSI Mentor
the-or-do-trap.pl
use strict;
use warnings;

my @tries = (
    [ 'dies'   => sub { die "boom\n" } ],
    [ 'ret 0'  => sub { 0 } ],
    [ 'ret 1'  => sub { 1 } ],
    [ 'ret ""' => sub { "" } ],
);

for my $t (@tries) {
    my ($label, $code) = @$t;

    my $returned = eval { $code->() };
    my $saved    = $@;

    # the common idiom, asked directly: does the handler run?
    my $fires = (eval { $code->() }) ? "no" : "YES";

    $saved =~ s/\s+\z//;
    printf "%-8s eval returned %-7s \$\@=%-8s 'or do' handler fires: %s\n",
        $label, defined $returned ? "'$returned'" : "undef", "'$saved'", $fires;
}
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
dies     eval returned undef   $@='boom'   'or do' handler fires: YES
ret 0    eval returned '0'     $@=''       'or do' handler fires: YES
ret 1    eval returned '1'     $@=''       'or do' handler fires: no
ret ""   eval returned ''      $@=''       'or do' handler fires: YES

Read the four rows:

  • dies — handler fires. Correct, and that is the case you wrote it for.
  • ret 0 — handler fires, and $@ is empty. Nothing died. The block simply returned a legitimate zero.
  • ret 1 — handler does not fire. Correct.
  • ret "" — handler fires. Again $@ is empty and nothing died.

So in two of four cases the error handler ran when there was no error. And these are not contrived values. A function that returns a count returns 0 when the count is zero. A function that returns a string returns "" for an empty field. Both are perfectly good answers, and both are reported as failures.

The fix is one character — make the block's last statement a constant truth:

Azvya Education Pvt. Ltd.VLSI Mentor
the-trailing-one.pl
use strict;
use warnings;

for my $value (0, "", 1) {
    my $ok = eval {
        my $result = $value;   # pretend this is real work that returned $value
        1;                     # <-- the whole point
    };
    printf "block returned %-5s -> handler fires: %s\n",
        "'$value'", $ok ? "no" : "YES";
}

print "---\n";

my $ok = eval { die "a real failure\n"; 1 };
my $e = $@; $e =~ s/\s+\z//;
printf "a real die          -> handler fires: %s  (\$\@='%s')\n", $ok ? "no" : "YES", $e;
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
block returned '0'   -> handler fires: no
block returned ''    -> handler fires: no
block returned '1'   -> handler fires: no
---
a real die          -> handler fires: YES  ($@='a real failure')

Now the handler fires if and only if something died. The 1; costs nothing, and every script in Chapter 14.3 used it for exactly this reason.

3. Block eval and String eval Are Different Tools

eval has two forms that share a name and do unrelated jobs.

eval { ... } — the block form. Compiled with the rest of your program, at compile time. This is the error-trapping tool and it is what you want in almost every case.

eval '...' — the string form. Takes a string, compiles it at run time, then runs it. It traps errors too, but its distinguishing feature is that it can compile code that did not exist when your script started.

Azvya Education Pvt. Ltd.VLSI Mentor
block-vs-string.pl
use strict;
use warnings;

# a string eval can survive text that is not even valid Perl
my $result = eval 'my $x = ;  1';
print "string eval returned: ", (defined $result ? "'$result'" : "undef"), "\n";

my $e = $@;
$e =~ s/\s+\z//;
$e =~ s/\(eval \d+\) line \d+/(eval) line N/;
print "string eval \$\@      : $e\n";

# a string eval also catches a strict violation at its own compile time
eval 'my $y = $never_declared_anywhere;  1';
print "strict violation caught: ", ($@ ? "yes" : "no"), "\n";

print "\nthe same broken text written as a BLOCK would not compile at all,\n";
print "so the whole file would fail before line 1 ever ran.\n";
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
string eval returned: undef
string eval $@      : syntax error at (eval) line N, at EOF
strict violation caught: yes

the same broken text written as a BLOCK would not compile at all,
so the whole file would fail before line 1 ever ran.

That is the real distinction. The string form caught a syntax error. A block eval cannot, because by the time your program runs, the block has already been compiled — broken syntax inside it stops the file at compile time, which is Chapter 14.1's first kind of failure and no run-time construct can trap it.

When to use the string form: almost never. The honest uses are narrow — checking whether an optional module is installed, or compiling a pattern or expression that genuinely comes from configuration at run time. Everything else it is reached for, it is the wrong tool for:

  • To trap errors — use the block form. It is faster and it cannot be confused by its own contents.
  • To call a function whose name is in a variable — use a code reference or $obj->$method(), from Chapter 12.12.
  • To build code from user input — do not. You are compiling a string somebody else controls.

4. What eval Does Not Catch

eval traps a die. It is easy to assume it traps everything, and it does not.

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

# 1. warn is not a death, so there is nothing to trap
eval { warn "just a warning\n"; 1 };
printf "after an eval containing warn: \$\@ = '%s' -> %s\n",
    $@, ($@ eq "" ? "NOT caught" : "caught");

# 2. exit is not a death either -- it ends the process
#    run it in a child so this script survives to report
my $dir = tempdir(CLEANUP => 1);
my $kid = File::Spec->catfile($dir, "kid.pl");
open my $w, ">", $kid or die "cannot write: $!\n";
print $w qq{print "A: start\\n";\neval { exit 4 };\nprint "B: after the eval\\n";\n};
close $w;

my $out = `perl "$kid"`;
my $rc  = $? >> 8;
$out =~ s/\s+\z//;
$out =~ s/\n/ | /g;
printf "child printed: %-12s exit %d -> exit was %s\n",
    "'$out'", $rc, ($rc == 4 ? "NOT caught" : "caught");
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
after an eval containing warn: $@ = '' -> NOT caught
child printed: 'A: start'   exit 4 -> exit was NOT caught

Both measured as not caught:

warn is not trappable by eval because it is not a death at all. The script keeps running either way, so eval has nothing to intervene in. To intercept warnings you need $SIG{__WARN__}, which is Chapter 14.11, or to promote them into real deaths, which is Chapter 14.6.

exit is not trappable either. The child printed A: start, the exit 4 ended the process immediately, and B never printed — the exit status was 4. This matters when you wrap a call to somebody else's code: a library that calls exit on a problem cannot be contained by your eval, and that is a reasonable thing to complain about in review.

Two more gaps worth knowing:

  • Compile errors in your own file are not catchable by anything at run time. Chapter 14.1's first category.
  • Most signals are not deaths. A SIGKILL cannot be handled at all, and others need $SIG{...} handlers rather than eval.

And one reassurance, since the opposite is sometimes claimed:

Azvya Education Pvt. Ltd.VLSI Mentor
die-message-truth.pl
use strict;
use warnings;

for my $case ([ 'die "0\\n"', sub { die "0\n" } ],
              [ 'die 0',      sub { die 0 } ],
              [ 'die ""',     sub { die "" } ]) {
    my ($label, $code) = @$case;
    eval { $code->() };
    my $raw = $@;
    $raw =~ s/ at .* line \d+\.?\s*\z//;
    $raw =~ s/\n/\\n/g;
    printf "%-11s -> \$\@ = %-8s boolean: %s\n", $label, "'$raw'", ($@ ? "true" : "FALSE");
}
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
die "0\n"   -> $@ = '0\n'    boolean: true
die 0       -> $@ = '0'      boolean: true
die ""      -> $@ = 'Died'   boolean: true

All three are true. Even die "" is safe, because Perl substitutes the string Died rather than leaving $@ empty. So for ordinary string messages, if ($@) does not miss a death. (The exception is an exception object whose class overloads boolean conversion to false — rare, and a reason to prefer Try::Tiny, which is Chapter 14.5.)

5. Adding Context By Re-throwing

The useful pattern once you have eval is to catch an error, add what you know, and throw it onward.

Azvya Education Pvt. Ltd.VLSI Mentor
rethrow.pl
use strict;
use warnings;

sub read_slack {
    my ($file) = @_;
    open my $fh, "<", $file or die "cannot open: $!\n";
    return 0;
}

my $outcome = eval {
    eval { read_slack("/no/such/report.rpt") };

    if (my $inner = $@) {
        $inner =~ s/\s+\z//;
        die "while loading the slack report: $inner\n";   # re-throw, richer
    }
    1;
};

my $final = $@;
$final =~ s/\s+\z//;
printf "outer eval returned: %s\n", defined $outcome ? "'$outcome'" : "undef";
printf "final \$\@ = '%s'\n", $final;
Azvya Education Pvt. Ltd.VLSI Mentor
Captured output
outer eval returned: undef
final $@ = 'while loading the slack report: cannot open: No such file or directory'

The outer eval returned undef and the message now carries both layers: what was being attempted, and what actually failed. The inner error is preserved rather than replaced, which is the difference between a useful error and a useless one.

Three practical notes on this pattern:

Copy $@ before you do anything with it. if (my $inner = $@) does the copy and the test in one step, which is why it is written that way here.

Keep the original text. Replacing cannot open: No such file or directory with could not load report throws away the only part that says why.

Do not wrap everything. An eval around code that cannot usefully recover just hides a failure you would rather see. Wrap the specific operation that has a sensible fallback.

6. What To Remember

  • eval { ... } traps a die so the script continues instead of stopping.
  • Statements after the die inside the block are skipped — eval abandons the rest of the block.
  • The message lands in $@, and a successful eval clears it, so copy it at once.
  • eval returns the block's last value, which is what or do actually tests.
  • Measured: eval { } or do { } fires the handler when nothing died — for a block returning 0 or "", two of four cases, with $@ empty.
  • End the block with 1; so the test means "did it die" rather than "was the answer truthy".
  • Or test $@ directly with nothing at all in between.
  • Block eval and string eval are different tools. The block form is compiled with your program; the string form compiles at run time.
  • Measured: only the string form catches a syntax error. The same text in a block stops the whole file at compile time.
  • Use the string form almost never — a code reference beats it for dynamic calls, and never compile user input.
  • Measured: eval does not catch warn (not a death) or exit (the child still exited 4 and the next line never ran).
  • Measured: $@ is true for every string message, including die "", which Perl turns into Died.
  • Re-throw to add context, copying $@ first and keeping the original text.

Chapter 14.5 replaces all of this ceremony with try, catch and finally. The measured comparison is the point: the same false-alarm case from section 2 behaves correctly without a trailing 1, and one widely repeated warning about Try::Tiny turns out to apply equally to plain eval.

Where this fits

Part of the Perl curriculum.