# Error state Four variables report errors from four different layers. They are not interchangeable and they are populated by entirely different mechanisms; misusing one for another is the most common cause of ”why doesn’t my error handling work“ bugs in Perl code. | Variable | Source | Lifetime | |------------|-----------------------------------------|-----------------------------------------------| | `$!` | C library `errno` from the last syscall | Set on syscall failure; meaningless otherwise | | `$@` | The exception caught by the last `eval` | Set on `eval` exit (empty string on success) | | `$?` | Exit status of the last child process | Set by every `wait`/pipe-close/`system` | | `$^E` | OS-extended error info | Same as `$!` on Unix; richer on VMS/Win32 | The single best thing you can do for your error handling is to internalise *which variable answers which question*: - ”Did my open/read/connect/syscall fail?“ → check the return value first, then `$!`. - ”Did my `eval` block catch an exception?“ → check `$@`. - ”What was the exit code of the program I just ran?“ → decode `$?`. - ”Am I on Windows or VMS and need OS-specific error text?“ → also look at `$^E`. ## `$!` — the system error `$!` is the Perl interface to the C library’s `errno`. It has a *dual nature*: read it in numeric context to get the integer `errno`, in string context to get the corresponding error string: ```perl open(my $fh, '<', '/no/such/file') or do { print "errno = ", $! + 0, "\n"; # numeric: 2 print "errstr = $!\n"; # string: "No such file or directory" }; ``` Crucially, `$!` is **only meaningful immediately after a failure**. The C `errno` is not cleared on success, so a later read might see a stale value from any earlier system call. The reliable shape: ```perl if (open my $fh, '<', $path) { # success — $! is meaningless here process($fh); } else { # ONLY here is $! meaningful die "open $path: $!"; } ``` Writing the failure on the same line as the operation that caused it is the safest spelling — it leaves no opportunity for an intervening Perl operation to clobber `errno`. ### `%!` — symbolic errno checks `%!` is a hash whose keys are errno symbol names like `ENOENT`, `EACCES`, `EAGAIN`. A key tests true exactly when `$!` currently equals that errno: ```perl unless (open my $fh, '<', $path) { if ($!{ENOENT}) { return []; # missing file → empty list } if ($!{EACCES}) { die "permission denied: $path"; } die "open $path: $!"; # anything else: re-raise } ``` This is portable — the numeric values of errno symbols differ across operating systems, but `%!` keys are always the symbol names. Behind the scenes, `%!` is provided by the [`Errno`](../../Errno.md) module, which is loaded on demand the first time you use the hash. ### Assigning to `$!` Writing to `$!` sets `errno` in the C library — useful for faking up an error message that downstream code will see: ```perl $! = 13; # EACCES print "$!\n"; # "Permission denied" # Slightly more idiomatic — set errno by symbol: use Errno qw(:POSIX); $! = EACCES; die "synthetic permission failure: $!"; ``` ## `$@` — the exception variable Set by [`eval`](../perlfunc/eval.md) when the block (or string) it ran threw an exception. It contains either the value passed to [`die`](../perlfunc/die.md) (which is most often a string, but can be any reference) or the parsing/runtime error message from the interpreter. ```perl eval { die "no port configured\n" unless defined $port; open(my $sock, '<', "/dev/tcp/$host/$port") or die "connect: $!"; 1; }; if ($@) { warn "could not open $host:$port — $@"; return; } ``` Successful evaluation sets `$@` to the empty string `''`. The `if ($@)` check is therefore reliable: empty string is false, any non-empty value is true. ### The classic `$@` clobbering bug There is a hazard here. Anything that runs *between* the `eval` exit and your `if ($@)` check can clobber `$@`. The most common offender is an object’s `DESTROY` method — when objects go out of scope at the close of the `eval` block, their `DESTROY` runs, and if `DESTROY` itself does any `eval`, your exception is gone: ```perl eval { my $obj = MyClass->new; $obj->might_die; 1; }; # If MyClass::DESTROY does an eval, $@ is now empty. warn "got: $@"; # may print nothing! ``` The historically-recommended fix is to capture `$@` immediately, or to use the `Try::Tiny` module, which handles this and several related pitfalls: ```perl my $err; { local $@; # save and isolate eval { risky_op(); 1; } or $err = $@ || 'unknown error'; } if ($err) { warn "got: $err"; } ``` The Perl 5.34+ native [`try`/`catch`](../perlfunc/try.md) feature is the cleaner modern spelling — it does not use `$@` at all and is not affected by the destructor problem: ```perl use feature 'try'; try { risky_op(); } catch ($err) { warn "got: $err"; } ``` PetaPerl supports `try`/`catch` natively. New code should prefer it. ### `eval` and `$!` `eval` does **not** preserve `$!`. If your `eval` block did a syscall that failed and then `die`d, `$!` is whatever the *last* operation set it to — which inside a destructor or [`local $SIG{__DIE__}`](signals.md) handler can be anything. If your error message needs `$!`, capture it inside the `eval`: ```perl eval { open(my $fh, '<', $path) or die "open $path: $!\n"; ... }; ``` The `\n` at the end is conventional — it suppresses the ”at line N“ suffix that Perl appends to die messages, which is right for user-facing errors. ## `$?` — the child error After a [`system`](../perlfunc/system.md), [`waitpid`](../perlfunc/waitpid.md), [`wait`](../perlfunc/wait.md), backtick command, or pipe close, `$?` contains the 16-bit `wait()` status of the child process. It is **not** simply the exit code: ```perl system($cmd, @args); my $status = $?; my $exit_code = $status >> 8; # exit value passed to exit() in the child my $signal = $status & 0x7F; # signal that killed the child, or 0 my $coredump = $status & 0x80; # set if the child dumped core if ($status == -1) { die "system: $!"; # could not even fork/exec } elsif ($signal) { die "$cmd died with signal $signal"; } elsif ($exit_code) { die "$cmd failed (exit $exit_code)"; } ``` The `>> 8` shift is the most-cited Perl gotcha there is. Only the upper byte is the exit code; the lower byte holds the signal-and-coredump flags. Forgetting the shift produces values multiplied by 256. Modern Perl provides the [`POSIX`](../../POSIX.md) module’s `WEXITSTATUS`, `WIFSIGNALED`, `WTERMSIG`, `WIFEXITED` macros, which are the portable named accessors: ```perl use POSIX ':sys_wait_h'; if (WIFEXITED($?)) { my $code = WEXITSTATUS($?); ... } if (WIFSIGNALED($?)) { my $sig = WTERMSIG($?); ... } ``` `$?` is **not** preserved across other operations. If you need the value, capture it into a local variable on the next line — otherwise the next backtick or `system` will overwrite it. ### `$?` in `END` blocks Inside an `END` block, `$?` contains the intended exit code. Assigning to it overrides the script’s exit status: ```perl END { $? = 0 if $? == 255 && $shutdown_was_clean; } ``` ## `$^E` — the extended OS error On Unix, `$^E` is exactly the same as `$!`. On Windows, it contains the `GetLastError()` value, which is sometimes more informative than `errno` (which Win32 maps a coarse subset onto). On VMS, it is the native VMS status code. PetaPerl is Linux-only. `$^E` and `$!` are interchangeable here; the variable exists for portable code that may also run on non-Unix perls. ## Putting them together — a worked example ```perl sub copy_file { my ($src, $dst) = @_; open my $in, '<', $src or die "open $src for reading: $!"; open my $out, '>', $dst or die "open $dst for writing: $!"; eval { local $/ = \65536; # block reads, scoped to this eval while (defined(my $chunk = <$in>)) { print {$out} $chunk or die "write $dst: $!"; } close $out or die "close $dst: $!"; 1; } or do { my $err = $@ || 'unknown error'; unlink $dst; # clean up the partial file close $in; # may set $! itself die "copy_file($src → $dst): $err"; }; close $in or warn "close $src: $!"; return 1; } ``` Three error variables in five paragraphs. `$!` reports each syscall failure on the line that triggered it. `$@` collects any exception that crossed the `eval` boundary. `$?` is not in this example because there is no child process — but if the function shelled out to `cp`, you would decode `$?` in the same shape as the [`system`](../perlfunc/system.md) page. ## See also - [`die`](../perlfunc/die.md) — populates `$@`. The usual way to raise an exception from your own code. - [`eval`](../perlfunc/eval.md) — catches exceptions; sets `$@`. - [`system`](../perlfunc/system.md), [`waitpid`](../perlfunc/waitpid.md), [`wait`](../perlfunc/wait.md) — populate `$?`. - [`try`/`catch`](../perlfunc/try.md) — the modern alternative to `eval`/`$@` that side-steps the clobbering hazard. - [`Errno`](../../Errno.md) — the source of `%!` keys; provides named errno constants. - `Try::Tiny` — robust `eval` wrapper that preserves `$@` correctly. - [`%SIG`](signals.md) — `$SIG{__DIE__}` and `$SIG{__WARN__}` are the hooks that fire when `die` and `warn` run; they can rewrite `$@`. - [Logical operators](../perlop/logical.md) — the `or die` idiom that drives almost all `$!` reporting.