Error Handling

Perl Intermediate/Advanced — Error Handling
Perl Intermediate/Advanced
Course 2 · Chapter 6 · Error Handling

🚨 Error Handling

Course 1's 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

die "Something went wrong"; # Something went wrong at script.pl line 12. — Perl appends " at FILE line N." automatically die "Something went wrong\n"; # Something went wrong — a trailing \n suppresses the automatic location text

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

eval { die "Something failed\n"; }; if ($@) { say "Caught: $@"; # Caught: Something failed }

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.

⚠ $@ Can Be Clobbered Before You Check It
eval { die "oops\n"; }; # if something else runs an eval, or a DESTROY method fires during garbage collection here... if ($@) { ... } # $@ might have been silently overwritten by then!

$@ 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+)

use feature 'try'; no warnings 'experimental::try'; try { die "Something failed\n"; } catch ($e) { say "Caught: $e"; # Caught: Something failed }

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

eval { die { code => 404, message => "Not found" }; # dying with a HASHREF, not a plain string }; if (my $err = $@) { say $err->{code}; # 404 — $@ IS that reference, not a stringified message }

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

use feature 'try'; no warnings 'experimental::try'; sub process_file { my ($filename) = @_; try { open(my $fh, '<', $filename) or die "Can't open $filename: $!\n"; while (my $line = <$fh>) { # ... process each line ... } close($fh); say "Processed $filename successfully"; } catch ($e) { say "Failed to process $filename: $e"; } }

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.

→ Solution

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.

→ Solution

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.

→ Solution

🎯 What's Next

Next chapter: Closures & Functional Techniques — anonymous subs, closures over lexicals, and map/grep/sort with custom blocks.