Error Handling
🚨 Error Handling
open(...) or die "..."; idiom raised errors — this chapter covers actually catching and handling them, from the classic eval/$@ approach through Perl's modern, genuinely cleaner try/catch syntax.
💀 die — Raising an Error
If a die message doesn't already end in a newline, Perl appends the file and line number automatically — genuinely useful for debugging, but the reason some error messages in real scripts show a location and others don't purely depends on whether the message ends in \n.
🎣 eval { } — Catching an Error
An eval { } block catches any die raised inside it — execution jumps straight to just after the block instead of terminating the program, and the special variable $@ holds whatever was passed to die (empty string if nothing went wrong). This is the classic, pre-5.34 way Perl has always done exception handling.
$@ is a global, and anything that runs between your eval and your check of $@ — including an unrelated eval elsewhere, or a destructor firing during garbage collection — can silently overwrite it. Best practice: capture it into your own variable immediately: my $err = $@; if ($err) { ... }.
✨ Modern try/catch (Perl 5.34+)
This is the same error caught the same way — but $e is a genuinely local, unambiguous variable scoped to the catch block, sidestepping every $@-clobbering concern from the warn-box above entirely. On Perl 5.34 or later, this is the recommended approach for new code.
🏗️ die with a Reference — Structured Exceptions
die accepts a reference just as readily as a string — $@ (or $e in the modern catch form) becomes that exact reference, letting catching code inspect structured data (an error code, a type, extra context) instead of pattern-matching a string message. This is a natural extension of Chapter 3/4's OOP: a real codebase often defines proper exception classes and dies with a blessed object instead of a plain hashref, giving catch code the ability to check ref($e) or call methods on the caught error directly.
🔧 A Worked Example — Robust File Processing
This combines Course 1, Chapter 8's file-handling idioms directly with modern try/catch — the open(...) or die pattern still triggers an error exactly as before, but now it's caught and handled gracefully by the surrounding try/catch instead of terminating the whole program.
Error Handling: Perl vs. Python/JavaScript
Perl's die/eval/$@ conceptually parallels Python's raise/except and JavaScript's throw/try/catch — all three "raise an exception, catch it somewhere up the call stack." Historically, Perl's eval-block approach was often described as a "poor man's" version, since it reused a block form originally meant for something else and relied on a global variable ($@) for the error itself. The modern try/catch syntax genuinely converges Perl with the dedicated exception-handling syntax Python and JavaScript have always had — cleaner, and no more global-variable clobbering concerns.
die
Raises an error; a trailing \n suppresses the automatic file/line info.
eval { } / $@
The classic catch mechanism — capture $@ immediately to avoid clobbering.
try / catch ($e)
Modern (5.34+), cleaner syntax — $e is safely scoped, no globals involved.
die with a reference
Structured exceptions — $@/$e becomes the reference itself.
💻 Coding Challenges
Challenge 1: Catch a Division Error
Write a subroutine safe_divide($a, $b) that dies with "Cannot divide by zero\n" if $b is 0. Call it inside a try/catch block with $b as 0, printing the caught error.
Challenge 2: The $@ Capture Pattern
Write an eval { } block that dies with a message, immediately capture $@ into my $err, and print it. Explain in one sentence why capturing it immediately is safer than checking $@ directly later.
Challenge 3: A Structured Exception
Write a subroutine that dies with a hashref { code => 400, message => "Bad input" } when given a negative number. Catch it and print both the code and message separately.
🎯 What's Next
Next chapter: Closures & Functional Techniques — anonymous subs, closures over lexicals, and map/grep/sort with custom blocks.