Control flow

catch#

Handle an exception thrown by a preceding try block.

catch is the second half of the try/catch construct. It is not a standalone statement: it follows a try block, declares a lexical VAR to receive the exception value, and runs its BLOCK only when the try block died. If the try block completed normally the catch block is skipped. Control then resumes at the statement following the whole construct (or the finally block, if one is present).

This syntax must be enabled with use feature 'try'. try and catch are non-experimental as of Perl 5.40.

Synopsis#

try BLOCK catch (VAR) BLOCK
try BLOCK catch (VAR) BLOCK finally BLOCK

What you get back#

catch is not a function call and has no return in the expression sense. Like other control-flow constructs, the value of the whole try/catch is the value of whichever block ran last — useful inside a do block or as the tail expression of a subroutine:

my $value = do {
    try {
        fetch_thing(@args);
    }
    catch ($e) {
        warn "fetch failed: $e";
        $DEFAULT;
    }
};

If try ran to completion, the construct yields the try block’s last value and VAR is never bound. If try died, the construct yields the catch block’s last value.

The VAR declaration#

The parentheses after catch are mandatory and must contain exactly one scalar variable declaration. It behaves like a my declaration — the my keyword is implicit, as in subroutine signatures:

catch ($e)  { ... }         # $e is a new lexical
catch (my $e) { ... }       # syntax error — 'my' is already implied

VAR is scoped to the catch block only. It is not visible in the preceding try block, in any following finally block, or after the construct. Its value is whatever the try block threw — a string, a list that stringified into one, or a reference, typically a blessed exception object. See die for how the exception value is produced.

Unlike eval, catch does not read or clear $@; the exception travels through VAR directly. $@ is untouched by the construct.

Global state it touches#

catch itself touches no special variables. The try/catch construct as a whole leaves $@ unchanged — a deliberate divergence from eval intended to stop callers from conflating the two idioms. If you need $@-style inspection, use eval instead.

Examples#

Catch a simple string exception and report it:

use feature 'try';

try {
    risky();
}
catch ($e) {
    warn "risky failed: $e";
}

Inspect a structured exception object:

try {
    process($record);
}
catch ($e) {
    if (ref($e) && $e->isa('My::NotFound')) {
        return;                       # missing is fine
    }
    die $e;                           # re-throw everything else
}

Re-throw unchanged with die. Because catch does not touch $@, you re-throw the variable you bound, not $@:

try {
    step();
}
catch ($e) {
    log_error($e);
    die $e;                           # propagates to the next handler
}

Produce a value from the construct without return. Using return inside try or catch returns from the enclosing subroutine, not from the construct — see Edge cases:

sub safe_parse {
    my ($text) = @_;
    try {
        parse($text);
    }
    catch ($e) {
        warn "parse error: $e";
        undef;                        # value of the catch block
    }
}

A catch block that itself dies — the new exception propagates outward to the next enclosing handler (try/catch, eval, or program exit):

try {
    open_db();
}
catch ($e) {
    cleanup();
    die "open_db failed: $e";         # thrown from inside catch
}

Edge cases#

  • Cannot stand alone. catch (VAR) { ... } without a preceding try block is a syntax error. catch is parsed as part of the try construct, not as an independent keyword.

  • Exactly one scalar in parentheses. The parentheses are mandatory, must declare exactly one scalar, and the my keyword is implied. catch { ... } without parentheses is a syntax error.

  • VAR is block-scoped. It exists only inside the catch block. A finally block that follows does not see it.

  • Falsy exceptions still trigger the handler. try distinguishes “died” from “completed normally” by a flag set at unwind time, not by truthiness of the exception value. die 0, die "", and die undef all enter the catch block; VAR receives the value as thrown (with die’s usual coercion of undef to "Died at ...").

  • $@ is not set. Code that reaches for $@ inside a catch block is reading the caller’s $@, not the exception just caught. Use VAR.

  • A catch block may die. Exceptions raised from inside catch are not re-caught by the same construct. They propagate to the next enclosing handler. This is the mechanism for re-throw (die $e) and for reporting cleanup failures from the handler itself.

  • return inside catch returns from the enclosing subroutine, not from the catch block. This differs from eval BLOCK, where return would leave only the eval. To yield a value from the construct, use the block’s final expression instead of return.

  • Loop controls (last, next, redo) inside catch target an enclosing loop, not the construct. The try/catch construct is not itself a loop.

  • caller() does not see the catch frame. Because catch does not intercept return, it is invisible to caller, like while or foreach are.

  • Pairs only with the immediately preceding try. You cannot have two catch blocks, nor place statements between try and catch. The optional finally block, if present, must come after catch.

Differences from upstream#

Fully compatible with upstream Perl 5.42.

See also#

  • try — the block whose failure this catch handles; always paired with exactly one catch

  • finally — optional third block that runs regardless of whether the try block died; cannot return or use loop controls

  • die — produces the exception value that VAR receives; use it inside catch to re-throw

  • eval — older exception-handling idiom using $@; reach for try/catch instead in new code for clearer scoping and no $@ footguns

  • $@ — left untouched by try/catch; inspect VAR instead