100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Exception Handling in Perl

Learn how Perl handles errors through die/eval, the modern try/catch feature, and object-based exceptions using Try::Tiny or custom exception classes.

Modules & OOPIntermediate9 min readJul 10, 2026
Analogies

The Basics: die and eval

Perl's original exception mechanism is 'die', which immediately unwinds the call stack and stops execution with an error message, and 'eval { ... }', which catches any 'die' that occurs inside its block and stores the error text in the special variable '$@' instead of letting the program crash. A bare 'die "Connection failed\n";' produces a plain string in $@, while 'die { code => 500, message => "Connection failed" };' can pass a reference (even a blessed object) as the exception payload, letting calling code inspect structured error data rather than parsing a string. Critically, you must check '$@' immediately after the eval block, because a subsequent operation (even something like calling another eval) can overwrite it before you read it.

🏏

Cricket analogy: A third umpire's review process stops play immediately when a decision is challenged (like 'die') and the outcome gets recorded on the review screen (like $@) for the on-field umpire to check right away before the next ball resets it.

perl
eval {
    open my $fh, '<', 'missing_file.txt'
        or die "Cannot open file: $!\n";
    # ... more risky work ...
    1;   # ensure a true value on success
} or do {
    my $error = $@;
    warn "Caught an error: $error";
    # handle or rethrow
};

# Modern equivalent (Perl 5.34+, no experimental warning needed since 5.36)
use feature 'try';
no warnings 'experimental::try';

try {
    open my $fh, '<', 'missing_file.txt'
        or die "Cannot open file: $!\n";
}
catch ($e) {
    warn "Caught an error: $e";
}
finally {
    print "Cleanup always runs here\n";
}

The Modern try/catch/finally Feature

Since Perl 5.34 (stabilized without an experimental warning in 5.36), the core 'feature "try"' pragma provides native 'try { } catch ($e) { }' syntax that reads much closer to exception handling in languages like Python or JavaScript, with an optional 'finally { }' block guaranteed to run whether or not an exception occurred, useful for closing file handles or releasing locks. Unlike bare eval/$@, the caught exception is automatically scoped to the '$e' variable named in the catch clause rather than relying on the global '$@', which avoids the classic bug where a nested eval silently clobbers an outer $@ before you read it. As of 2026 this native syntax is considered stable and is the recommended default for new code on Perl 5.36+, while Try::Tiny remains common in codebases that must support older Perl versions.

🏏

Cricket analogy: DRS (Decision Review System) giving a dedicated, isolated replay screen for each specific review rather than one shared monitor everyone might overwrite mirrors 'catch ($e)' scoping the error to its own lexical variable instead of the shared global $@.

Object-Oriented Exceptions

Passing a plain string to 'die' is fine for quick scripts, but production code benefits from throwing blessed exception objects because callers can then use 'ref($e)' or 'isa()' to programmatically distinguish an out-of-stock error from a payment-declined error, rather than pattern-matching an error string with a fragile regex. Frameworks like Throwable::Error, Exception::Class, or a hand-rolled Moose/Moo class with attributes for 'message', 'code', and 'retryable' let calling code write 'catch ($e) { if ($e->isa("MyApp::Exception::NotFound")) { ... } elsif ($e->isa("MyApp::Exception::Timeout")) { ... } }', producing exception handling that mirrors the typed exception hierarchies familiar from Java or Python. This also composes cleanly with 'finally' blocks and rethrowing: 'catch ($e) { $e->isa("Retryable") ? retry() : die $e; }' propagates the original object upward instead of losing type information by stringifying it.

🏏

Cricket analogy: An umpire distinguishing between a 'no-ball' signal and a 'wide' signal using distinct, unmistakable hand gestures rather than a vague shout mirrors distinguishing MyApp::Exception::NotFound from MyApp::Exception::Timeout via isa() instead of parsing an error string.

Since Perl 5.34, 'die' without a trailing newline automatically appends the file name and line number to the error message (e.g. 'Connection failed at db.pl line 42.'); adding your own trailing "\n" suppresses that suffix, which is why library code that wants clean, predictable error text always ends its die messages with "\n".

An 'eval { }' block without a final truthy statement can silently report success even when the block's last expression evaluates to false without dying — always end risky eval blocks with an explicit '1;' as the last statement, or check $@ rather than relying solely on the block's return value, to avoid mistaking a falsy-but-non-dying result for an error or vice versa.

  • 'die' halts execution and unwinds the stack; 'eval { }' catches it and stores the error in $@.
  • die can throw a plain string or a reference/blessed object, enabling structured error data.
  • $@ must be read immediately after eval, since a later operation can silently overwrite it.
  • Native 'try { } catch ($e) { } finally { }' (stable since Perl 5.36) scopes errors to $e, avoiding $@ clobbering.
  • Blessed exception objects (via Exception::Class, Throwable::Error, or Moose/Moo) let callers dispatch with isa() instead of parsing strings.
  • Ending a die message with "\n" suppresses Perl's automatic 'at FILE line N' suffix.
  • Always end an eval block with a truthy final statement (like '1;') to avoid conflating a falsy result with a real error.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#PerlStudyNotes#ExceptionHandlingInPerl#Exception#Handling#Perl#Die#ErrorHandling#StudyNotes#SkillVeris