I/O

warn#

Emit a warning to STDERR.

warn stringifies LIST and prints the result to STDERR — the same formatting machinery as die, minus the unwind. Control returns to the caller; nothing is thrown, nothing is caught. Reach for it when the program should keep running but the user, the operator, or the log deserves to hear about something unexpected.

Synopsis#

warn LIST
warn $message
warn                         # re-surface current $@ as a warning

What you get back#

warn returns 1 under normal circumstances. The return value is almost never useful — the point of the call is the side effect on STDERR (or on whatever $SIG{__WARN__} does with the message).

Unlike die, warn does not unwind. Execution continues with the statement after the call.

Global state it touches#

  • $@ — read by the no-argument form. If $@ holds a non-empty value, warn re-surfaces it (see Propagation below). warn does not write $@.

  • $,, $\not consulted. warn is not print; the output separators and record separator play no role. The message is written as a single string.

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

  • $SIG{__WARN__} — if set, the handler runs in place of writing to STDERR. See The $SIG{__WARN__} hook below.

  • STDERR — the default destination. Any redirection of STDERR (shell-level 2>, open STDERR, ..., a PerlIO layer) affects where warn output lands.

The trailing-newline rule#

The same rule as die applies, and for the same reason:

  • Message ends in "\n" — used verbatim. Nothing is appended. Use this for warnings that are complete user-facing diagnostics.

  • 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 when pinpointing the call site is more useful than a tidy message.

warn "cache is stale\n";            # "cache is stale\n"
warn "cache is stale";              # "cache is stale at main.pl line 42.\n"

Reference-valued warnings never get location information appended. Stringifying an object to add file and line would defeat the point of passing one.

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

When LIST is empty or stringifies to "", warn does not emit a fresh message — it re-surfaces whatever is in $@:

  • If $@ is non-empty, warn appends "\t...caught" to it and emits the result. This is the canonical way to notice that an exception was caught without letting it die silently:

    eval { risky() };
    warn if $@;                     # "<whatever died>\n\t...caught at ..."
    
  • If $@ is also empty, the string "Warning: Something's wrong" is used.

This is deliberately asymmetric with die: die with no argument re-throws (triggering the propagation machinery and the PROPAGATE hook for objects); warn with no argument reports what is already in $@.

The $SIG{__WARN__} hook#

Setting $SIG{__WARN__} installs a handler that runs instead of the default write to STDERR. The handler receives the exception derived from LIST as its single argument and is responsible for disposing of it — logging it, rewriting it, escalating it to die, or dropping it on the floor:

local $SIG{__WARN__} = sub {
    my ($msg) = @_;
    return if $msg =~ /^Use of uninitialized/;  # silently drop
    log_warning($msg);
};

Three asymmetries with $SIG{__DIE__} are worth internalising:

  • The default message is suppressed. $SIG{__DIE__} runs alongside the exception machinery; $SIG{__WARN__} runs instead of the default STDERR write. If the handler does nothing, the warning is silently dropped.

  • To pass the warning through, call warn again. The handler is not re-entered for its own warn call, so there is no infinite loop:

    local $SIG{__WARN__} = sub {
        log_warning($_[0]);
        warn $_[0];                  # re-emit to STDERR as well
    };
    
  • A __WARN__ handler can silence mandatory warnings — warnings that no warnings cannot turn off. This makes $SIG{__WARN__} a sledgehammer; prefer no warnings '<category>' for targeted suppression and reserve the handler for routing or escalation.

# wipe out *all* compile-time warnings, then switch on at runtime
BEGIN { $SIG{'__WARN__'} = sub { warn $_[0] if $DOWARN } }
my $foo = 10;
my $foo = 20;                       # duplicate-my silenced
$DOWARN = 1;
warn "\$foo is alive and $foo";     # now shows up

warn the built-in vs. use warnings#

These are different things. Do not confuse them:

  • warn is a built-in function that emits a single message right now. You call it.

  • use warnings is a pragma that enables categories of compile-time and run-time diagnostics (uninitialized values, deprecated features, unopened filehandles, and so on). The compiler and interpreter emit those diagnostics on your behalf, using the same machinery warn uses.

The pragma’s diagnostics flow through $SIG{__WARN__} exactly like explicit warn calls. no warnings '<category>' suppresses the pragma’s diagnostics in its lexical scope but has no effect on an explicit warn you wrote yourself — that call always fires.

Examples#

Report a recoverable problem and keep going:

open my $fh, "<", $path
    or warn "skipping $path: $!\n" and next;

Development-mode diagnostic with location appended:

warn "unexpected record shape";
# unexpected record shape at parser.pl line 87.

Noticing a caught exception without suppressing it:

eval { load_config() };
warn if $@;                         # surfaces "...caught at ..."

Route every warning through a logger, still printing to STDERR:

local $SIG{__WARN__} = sub {
    my ($msg) = @_;
    $logger->warn($msg);
    print STDERR $msg;              # preserve default visibility
};

Escalate a specific warning to a fatal error:

local $SIG{__WARN__} = sub {
    die @_ if $_[0] =~ /corrupt database/;
    warn @_;                        # everything else: default path
};

Pass a blessed object for structured warnings (the receiver of a $SIG{__WARN__} hook gets the reference, not a stringified form):

local $SIG{__WARN__} = sub {
    my ($w) = @_;
    if (ref($w) && $w->isa('MyApp::Warning')) {
        $logger->record($w);
    }
    else {
        print STDERR $w;
    }
};
warn MyApp::Warning->new(code => 'SLOW_IO', detail => $path);

Edge cases#

  • List with two or more items is stringified and concatenated: warn "bad record ", $n, ": ", $raw. The newline rule applies to the concatenated result.

  • Empty list (warn; or warn "";) triggers the propagation path, not a fresh message.

  • Reference-valued warning inside a $SIG{__WARN__} handler: the handler receives the reference unchanged. If no handler is installed, the reference is stringified to STDERR via its "" overload (or Perl’s default MyClass=HASH(0x...)).

  • warn inside $SIG{__WARN__}: does not re-enter the handler. The call writes to STDERR as if no handler were installed. This is what makes the “pass through” idiom safe.

  • warn during global destruction: still works, but STDERR may already be closed in pathological cases. Warnings from DESTROY handlers running at interpreter exit can vanish.

  • STDERR closed or redirected: warn writes to whatever STDERR currently points at. If STDERR is closed, the write fails silently; no error is raised, because warn itself does not check.

  • No relation to $\ / $,: adding local $\ = "\n" does not make warn "x" print a newline. The trailing-newline rule is the only newline-adding mechanism for warn.

Differences from upstream#

Fully compatible with upstream Perl 5.42.

See also#

  • die — same formatting rules (trailing-newline suppresses location, references pass through unchanged), but raises an exception instead of reporting and continuing

  • eval — pairs with the no-argument form of warn for the “catch, inspect, re-surface” idiom

  • $@ — the source the no-argument warn reports from

  • $SIG{__WARN__} — the hook that replaces the default STDERR write; use it for routing, filtering, or escalation

  • Carpcarp, cluck, croak, confess: warnings and exceptions that report from the caller’s perspective rather than the call site, which is almost always what a library wants