try/catch
try runs a block. If an error happens anywhere inside it (in a built-in command, a custom command, an error make, or a failing external command), the rest of the block is skipped and the optional catch closure runs instead. An optional finally closure runs afterwards in every case.
try { <block> } catch {|err| <on error> } finally {|value| <always> }Both catch and finally are optional, but if both are given, catch comes first.
See also: try command help, Control Flow - try and Creating Your Own Errors in the Book.
let n = try { "abc" | into int } catch { 0 }
$n
# => 0The Result of try
| Outcome | Value of the try expression |
|---|---|
| The block succeeds | The block's value |
An error occurs, catch is given | The catch closure's value |
An error occurs, no catch | null (the error is swallowed) |
An error occurs, only finally | The error is re-raised after finally runs |
The output of finally is always discarded.
try { 1 / 0 } | describe
# => nothingThe Error Record
The catch closure receives a record describing the error, both as its parameter and as $in:
| Field | Contents |
|---|---|
msg | The error message |
debug | The Rust debug representation of the error |
raw | The error itself. Returning it from catch re-raises the original error |
rendered | The error as Nushell would print it (a string) |
details | A record with msg, labels, code, url, help and inner |
exit_code | Only for failed external commands: the exit code |
try { 1 / 0 } catch {|err| [$err.msg $err.details.code] }
# => ╭───┬─────────────────────────────╮
# => │ 0 │ Division by zero. │
# => │ 1 │ nu::shell::division_by_zero │
# => ╰───┴─────────────────────────────╯try { error make {msg: "disk full", help: "free some space"} } catch { $in.details | select msg help }
# => ╭──────┬─────────────────╮
# => │ msg │ disk full │
# => │ help │ free some space │
# => ╰──────┴─────────────────╯Language Notes
A non-zero exit code from an external command is an error, including one in the middle of a pipeline. The error record then has an
exit_codefield:try { ^false | lines } catch {|e| $e.exit_code } # => 1Errors raised inside
catchpropagate normally. Whencatchraises a new error witherror make, the original error is attached to it and printed after it:try { error make {msg: a} } catch {|e| error make {msg: b} } # => Error: nu::shell::error # => # => × b # => ╭─[repl_entry #1:1:51] # => 1 │ try { error make {msg: a} } catch {|e| error make {msg: b} } # => · ──────── # => ╰──── # => # => Error: # => × a # => ╭─[repl_entry #1:1:18] # => 1 │ try { error make {msg: a} } catch {|e| error make {msg: b} } # => · ──────── # => ╰────To log an error and re-raise it unchanged, return
$e.raw:catch {|e| print "logging"; $e.raw }.finallyruns after the block succeeds, aftercatch, when an uncaught error is about to propagate, and whenbreak,continue,returnorexitleaves thetry(onlyexit --abortskips it). It receives a value as its parameter and as$in:- on success, or after
catch, the value of the block or ofcatch; - on an error that was not caught, the error record;
- after
break,continueorreturn,nothing.
try { "ok" } catch { "failed" } finally {|v| print $"finally got: ($v)" } # => finally got: ok # => oktry { error make {msg: boom} } finally {|v| print $"cleaning up after: ($v.msg)" } # => cleaning up after: boom # => Error: nu::shell::error # => # => × boom # => ╭─[repl_entry #1:1:18] # => 1 │ try { error make {msg: boom} } finally {|v| print $"cleaning up after: ($v.msg)" } # => · ─────────── # => ╰────- on success, or after
The
trybody is a block, so it can updatemutvariables.catchandfinallyare closures, so they cannot:mut x = 0; try { error make {msg: a} } catch { $x = 1 } # => Error: nu::parser::expected_keyword # => # => × Capture of mutable variable. # => ╭─[repl_entry #1:1:48] # => 1 │ mut x = 0; try { error make {msg: a} } catch { $x = 1 } # => · ─┬ # => · ╰── capture of mutable variable # => ╰────Assign the result of the whole
tryexpression instead:$x = try { ... } catch { 1 }.trypasses its pipeline input to the block as$in:[1 2 3] | try { math sum } catch { 0 } # => 6Parse errors cannot be caught. The whole entry or script is parsed before anything runs, so a syntax error inside a
tryblock stops everything:try { 1 + } catch { "never reached" } # => Error: nu::parser::incomplete_math_expression # => # => × Incomplete math expression. # => ╭─[repl_entry #1:1:9] # => 1 │ try { 1 + } catch { "never reached" } # => · ┬ # => · ╰── incomplete math expression # => ╰────For external commands,
completeis an alternative when you want the exit code and output without treating failure as an error.do --ignore-errorsruns a closure and turns any error intonull.