nushell icon indicating copy to clipboard operation
nushell copied to clipboard

error_make: Another rewrite of `error make`

Open KaiSforza opened this issue 1 month ago • 1 comments

User-facing Changes

  • New arguments! (error make "hello")
  • New parts for error_struct! (error make {inner: [] labels: [] ...})
  • Pipeline inputs for chained errors! (try {error make foo} catch {error make bar})
  • Pipeline inputs for normal errors! ("help" | error make)
  • External errors! (error make {src: {path: $nu.cofig-path} ...})
  • Backwards compatibility!

Arguments and Inputs

The main changes are in how the arguments are passed. Everything is still backwards compatible with the old error make commands, there's just a nice extra layer we get from the pipeline and a few new args (that were already added in #17037). There are some new ways to (hopefully intentionally) cause an error, such as using a naked error make, pipelines from records and simple string input!

Inputs

Because error make will just make an error anyway, it can technically take any input to make an error, but only properly formatted record input will create a chain. the x | f $in pattern can be used for string input, if that is more comfortable.

With no arguments

This is a completely new way to do this, with no arguments the error make invocation is highlighted, along with a simple originates from here message. This makes normal errors very easy to create without any special message setup.

> error make
Error: nu::shell::error

  × originates from here
   ╭─[entry #4:1:1]
 1 │ error make
   · ──────────
   ╰────

Create a single argument

  • With pipeline input: {msg: foo} | error make
  • With an argument: error make {msg: foo}
  • With a string argument: error make foo
Error: nu::shell::error

  × foo
   ╭─[entry #2:1:12]
 1 │ error make {msg: foo}
   ·            ──────────
   ╰────

Chaining errors together

These will automatically create a chain of errors, placing the pipeline as an inner to the argument. This can very easily be used to get a bit more detail in a try loop using the naked error make:

Error: nu::shell::error

  × originates from here
   ╭─[source:1:31]
 1 │ try {error make "foo"} catch {error make}
   ·                               ──────────
   ╰────

Error: nu::shell::error

  × foo
   ╭─[source:1:6]
 1 │ try {error make "foo"} catch {error make}
   ·      ──────────
   ╰────

Or with more complex errors:

  • With both, combining the errors: {msg: foo} | error make bar
  • With the raw error from try: try {error make foo} catch {error make bar}

Both are equivalent to:

  • error make {msg: bar inner: [{msg: foo}]}
Error: nu::shell::error

  × bar
   ╭─[entry #1:1:29]
 1 │ try {error make foo} catch {error make bar}
   ·                             ──────────
   ╰────

Error: nu::shell::error

  × foo
   ╭─[entry #1:1:6]
 1 │ try {error make foo} catch {error make bar}
   ·      ──────────
   ╰────

Labels

As is noticeable in the examples above, simple errors no longer use an extra line for the label. If no label is present, error make will place a bar under the span of itself or the argument to error make. Labels have also gotten a bit of a rewrite, but they're pretty much the same as those in #17037, except for label, which is now only a single label (not oneof<list, label>).

Simple Labels

label.text and labels.*.text is no longer required for a span to show up, an empty text will simply underline. This example can either use label: $record or be written as labels: [$record]:

> def f [x] {
  error make {msg: here label: {span: (metadata $x).span}}
}
f abcd
Error: nu::shell::error

  × here
   ╭─[entry #7:4:3]
 3 │ }
 4 │ f abcd
   ·   ────
   ╰────

Multiple labels

Any number of labels can be added in the labels column, allowing for more detailed error messages, especially for functions:

> def f [x y z] {
  error make {msg: here labels: [
    {text: "there" span: (metadata $x).span}
    {text: "everywhere" span: (metadata $y).span}
    {text: "somewhere" span: (metadata $z).span}
  ]
  }
}
f abcd [x y z] {d: a}

Error: nu::shell::error

  × here
   ╭─[entry #11:9:3]
 8 │ }
 9 │ f abcd [x y z] {d: a}
   ·   ──┬─ ───┬─── ───┬──
   ·     │     │       ╰── somewhere
   ·     │     ╰── everywhere
   ·     ╰── there
   ╰────

External sources

There is a ShellError::OutsideSpannedLabeledError that can be used to refer to external sources, not just the internal nushell spanns. This has been expanded to allow the multi-label stuff to work using the new src column:

> "foo\nbar\nbaz" | save -f /tmp/foo.bar
error make {
  msg: 'error here'
  src: {path: /tmp/foo.bar}
  labels: [
    {text: "this" span: {start: 4 end: 7}}
  ]
}
Error: nu::shell::outside

  × error here
   ╭─[/tmp/foo.bar:2:1]
 1 │ foo
 2 │ bar
   · ─┬─
   ·  ╰── this
 3 │ baz
   ╰────

Errors That Can't be Caught

These will not work since try will never get parsed:

  • try {1 + ""} catch {error make badmath}
  • (TODO: Add more examples)

Internal Changes

Most of the parsing from an error record to an actual error is now moved into nu-protocol, using FromValue to turn it into a useful internal type.

nu-protocol::LabeledError

This struct has a few changes, the main one being the type of LabeledError.inner. It is now a ShellError, not another LabeledError. It should be trivial to do a .into() for things that already use LabeledError.with_inner(x).

nu-protocol::ShellError::into_value

I renamed the old into_value to into_full_value to better say what it is, since it doesn't just do the IntoValue::into_value method, it also requires some context to create the Value. Now ShellError has an IntoValue implementation matching other types.

nu-protocol::ShellError::{OutsideSource, OutsideSourceNoUrl}

Miette's derived types don't have a nice way to maybe include a url, so there are now two types! These allow using multiple labels on outside sources. They are used internally for the new {src: {}} part of the error_struct, and they look a lot more like the LabeledError, but without the need for a separate type and all the fun impls that would require for the Diagnostic::source_code method.

Misc

  • Spelling fix: into_chainned => into_chained

Current bugs:

  • [x] OutsideSpannedLabeledError
    The inner most error of try {']' from nuon} catch {error make} will reference span: {start: 0, end: 1}, which in ']' from nuon will point to the ] character, but when it does this in error make as an input it will point to the very first character (probably the n in nu).

Release notes summary - What our users need to know

New error make functionality!

  • New arguments! (error make "hello")
  • New parts for error_struct! (error make {inner: [] labels: [] ...})
  • Pipeline inputs for chained errors! (try {error make foo} catch {error make bar})
  • Pipeline inputs for normal errors! ("help" | error make)
  • External errors! (error make {src: {path: $nu.cofig-path} ...})
  • Backwards compatibility!

Tasks after submitting

KaiSforza avatar Nov 24 '25 23:11 KaiSforza

Removed some of the stuff that I added for testing.

KaiSforza avatar Nov 30 '25 21:11 KaiSforza

Thanks

fdncred avatar Dec 18 '25 13:12 fdncred