I/O · Control flow

die#

Raise an exception.

die throws an exception built from LIST. If the exception happens inside an eval, the eval terminates with undef and $@ holds the exception value. If nothing catches it, Perl prints the message to STDERR and the interpreter exits with a non-zero status. The exception can be a plain string, a list that stringifies into one, or a reference — typically a blessed object carrying structured error state.

Synopsis#

die LIST
die $message
die $exception_object
die                         # re-raise / propagate current $@

What you get back#

die does not return. Control transfers to the nearest dynamically enclosing eval (which then returns undef in scalar context or the empty list in list context), or — if there is no enclosing eval — the interpreter unwinds, runs END and DESTROY handlers, and exits.

The exception value itself lands in $@:

  • String exceptions — the stringified message, including any appended at FILE line N.\n (see below).

  • Reference exceptions — the reference itself, unchanged. This is the idiomatic way to throw a blessed exception object.

On uncaught exit, the exit code is derived from $! and $?:

exit $! if $!;              # errno
exit $? >> 8 if $? >> 8;    # child exit status
exit 255;                   # last resort

Rely only on the exit code being non-zero; the exact value is a best-effort encoding of whatever global error state was live at the moment of the throw.

Global state it touches#

  • $@ — written with the exception on unwind into an eval, read by the no-argument form of die to propagate an existing exception, and read by the uncaught-exception printer.

  • $! — read when deriving an exit code for an uncaught exception, and commonly interpolated into the message itself (die "open $f: $!").

  • $? — read when deriving an exit code if $! is zero. END and DESTROY handlers can still mutate it before the process actually exits.

  • $. — the current input line number, appended to string exceptions that lack a trailing newline.

  • $SIG{__DIE__} — if set, the handler runs before the exception is propagated, with the exception as its argument. The handler can replace the exception by calling die again with a different value.

  • $^S — readable by $SIG{__DIE__} handlers to tell whether the die is happening inside an eval.

The trailing-newline rule#

Whether die appends location information depends on one character at the end of the message:

  • Message ends in "\n" — used verbatim. Nothing is appended. Use this form when the message is already a complete, user-facing diagnostic.

  • Message does not end in "\n" — Perl appends " at FILE line N", and if a file is being read, ", <HANDLE> line M", then a final "." and newline. Use this form during development, or whenever locating the die call in the source is more useful than a tidy message.

die "bad config\n";                 # "bad config\n"
die "bad config";                   # "bad config at script.pl line 42.\n"

A common idiom is to append ", stopped" to make the location suffix read naturally:

die "/etc/games is no good, stopped";
# /etc/games is no good, stopped at canasta line 123.

Reference exceptions never get location information appended — stringifying an object to inspect it would defeat the point of throwing an object. If you want file and line inside a structured exception, capture them at construction time (__FILE__, __LINE__, or caller).

Reference-valued exceptions#

Passing a reference — typically a blessed object — lets $@ carry structured state instead of a string you later have to parse:

package MyApp::Error;
sub new   { my ($c, %a) = @_; bless { %a }, $c }
sub code  { $_[0]{code}  }
sub where { $_[0]{where} }

eval {
    die MyApp::Error->new(code => 'EPARSE', where => 'line 12');
};
if (my $err = $@) {
    if (ref($err) && $err->isa('MyApp::Error')) {
        warn "parse error at ", $err->where;
    }
    else {
        die $err;   # not ours — re-raise
    }
}

Overload stringification on exception classes so that an uncaught exception still prints a useful message:

use overload '""' => sub { "[MyApp::Error ".$_[0]->code."]\n" };

The stringified form should end in a newline and be non-empty, matching the treatment of string exceptions.

Because $@ is a global, copy it into a lexical before inspecting it — any method call, ref, or isa you run during handling can itself eval internally and clobber $@:

if (my $err = $@) { ... }       # safe
if ($@) { $@->method; ... }     # unsafe: method may reset $@

Propagation: die with no argument (or empty string)#

When LIST is empty or stringifies to "", die does not throw a fresh exception — it re-raises whatever is in $@:

  • If $@ holds a string, die appends "\t...propagated at FILE line N.\n" and re-throws. This is the canonical way to let an exception continue up the call stack after a partial-match check:

    eval { risky() };
    die unless $@ =~ /Expected exception/;
    
  • If $@ holds an object reference with a PROPAGATE method, that method is invoked as $@ = eval { $@->PROPAGATE(__FILE__, __LINE__) }, and the return value replaces $@ before the re-throw. Implement PROPAGATE on an exception class when you want the exception to record the chain of re-raises.

  • If $@ holds an object reference with no PROPAGATE, the reference is re-thrown unchanged.

  • If $@ is also empty, the string "Died" is used.

The $SIG{__DIE__} hook#

Setting $SIG{__DIE__} installs a handler that runs when die fires, with the exception as its argument. The handler can inspect or replace the exception by calling die again with a different value. It runs even inside eval, which is the usual reason to want it out of the way:

local $SIG{__DIE__} = sub {
    die @_ if $^S;              # inside eval: do nothing special
    log_fatal(@_);              # outside eval: log before we exit
};

$^S is undef during compilation, true inside an eval, and false at the top level — use it to gate handler work that only makes sense for uncaught exceptions.

Examples#

Bail out with a clean message (note the trailing newline suppresses file/line appending):

chdir '/var/spool' or die "can't cd to spool: $!\n";

Die without a newline during development so the location is appended:

die "unexpected empty record";
# unexpected empty record at parse.pl line 87.

Catch a string exception:

eval {
    parse($input);
};
if ($@) {
    warn "parse failed: $@";    # $@ already ends in "\n" if the
                                # die'd message did
}

Throw and catch a blessed exception object:

eval {
    die MyApp::Error->new(code => 'EIO', detail => $!);
};
if (my $err = $@) {
    if (ref($err) && $err->isa('MyApp::Error')) {
        handle($err);
    }
    else {
        die $err;               # re-raise anything unexpected
    }
}

Conditional propagation — catch one class, let the rest through:

eval { work() };
die unless $@ =~ /^recoverable:/;
recover();

Edge cases#

  • List with two or more items is stringified and concatenated to form the message: die "read failed on ", $file, ": $!". The newline rule applies to the concatenated result.

  • Empty list (die; or die "";) triggers the propagation path described above, not a fresh exception.

  • Reference with trailing newline inside the stringified form is irrelevant: location is never appended to a reference, regardless of what its "" overload produces.

  • die inside DESTROY during global destruction can still fire $SIG{__DIE__} but may not propagate as expected — the interpreter is already unwinding. Keep destructors defensive.

  • Uncaught die during END block runs remaining END blocks but sets exit status per the $!/$? rule above.

  • Nested eval and $SIG{__DIE__}: the handler runs for every die, including those caught by an inner eval. Guard with $^S if the handler has side effects.

  • Re-throwing the current $@: prefer bare die; over die $@;. The bare form triggers the propagation machinery (...propagated suffix, PROPAGATE hook); die $@ does not — it treats $@ as a fresh exception value.

Differences from upstream#

Fully compatible with upstream Perl 5.42.

See also#

  • eval — the only construct that catches a die; its block form traps exceptions into $@

  • warn — same formatting rules (trailing-newline suppresses location), but writes to STDERR and does not unwind

  • caller — capture file and line at throw time when building a structured exception object

  • exit — use when you want a specific exit code without the exception-handling machinery

  • $@ — where the exception lands after an eval catches it, and the source the no-arg die re-raises from

  • $SIG{__DIE__} — pre-throw hook that can inspect or rewrite the exception

  • $^S — tells a $SIG{__DIE__} handler whether it is running inside an eval