Skip to content

Add all LabeledError structure parts to error make#17037

Merged
fdncred merged 6 commits intonushell:mainfrom
KaiSforza:kai/error-more
Nov 20, 2025
Merged

Add all LabeledError structure parts to error make#17037
fdncred merged 6 commits intonushell:mainfrom
KaiSforza:kai/error-more

Conversation

@KaiSforza
Copy link
Copy Markdown
Contributor

@KaiSforza KaiSforza commented Nov 17, 2025

This allows all of the inputs to a LabeledError structure to be accessed from inside of Nushell scripts, not just plugins, including:

  • multiple labels (see error from 1 + "")
  • errors inside of errors (see error from ']' | from nuon)
  • custom codes instead of only nu::shell::error
  • URL's added to errors using the url key

This is a complete rewrite of the error make command using FromValue to simplify the parsing a lot. I did have to write a FromValue implementation for nu_protocol::Span that goes from a record into a Span object. The error checking happens in there instead of in error make now.

Here are a few examples:

> def foo [pond sink] {
    error make {
        msg: "this is fishy"
        code: "my::error"
        label: [
            {text: "fish right here" span: (metadata $pond).span}
            {text: "don't fish here" span: (metadata $sink).span}
        ]
        help: "something to tell the user as help"
        url: "https://nushell.sh"
    }
}
> foo "pond" "sink"
Error: my::error (link)

  × this is fishy
   ╭─[entry #2:1:5]
 1 │ foo "pond" "sink"
   ·     ───┬── ───┬──
   ·        │      ╰── don't fish here
   ·        ╰── fish right here
   ╰────
  help: something to tell the user as help
> try {
    foo pond "not a pond"
} catch {|e|
    error make {
        msg: "An outer error"
        inner: [($e.json | from json)]
    }
}
Error: nu::shell::error

  × An outer error
   ╭─[entry #3:4:5]
 3 │ } catch {|e|
 4 │     error make {
   ·     ─────┬────
   ·          ╰── originates from here
 5 │         msg: "An outer error"
   ╰────

Error: my::error (link)

  × this is fishy
   ╭─[entry #3:2:9]
 1 │ try {
 2 │     foo pond "not a pond"
   ·         ──┬─ ──────┬─────
   ·           │        ╰── don't fish here
   ·           ╰── fish right here
 3 │ } catch {|e|
   ╰────
  help: something to tell the user as help

The (link) is clickable if the right $env.config settings are enabled, or it will print https://example.com to the screen.

Release notes summary - What our users need to know

  • Add the all parts of the LabeledError structure to the error_struct argument of error make

Tasks after submitting

Added tests for these new inputs, changed the help output.
@fdncred
Copy link
Copy Markdown
Contributor

fdncred commented Nov 18, 2025

It would be good to have something in the release notes summary that can go in the release notes

@KaiSforza KaiSforza changed the title Add url and code options to error make Add all LabeledError structure parts to error make Nov 19, 2025
@KaiSforza KaiSforza marked this pull request as draft November 19, 2025 19:05
@KaiSforza KaiSforza marked this pull request as ready for review November 19, 2025 19:39
@KaiSforza
Copy link
Copy Markdown
Contributor Author

Did a quick rewrite as a separate commit. To me it's a lot cleaner, but it does change the error messages for failed parsing on some of the elements.

@fdncred
Copy link
Copy Markdown
Contributor

fdncred commented Nov 20, 2025

Are we ready to move forward with this? @cptpiepmatz did you want to look at this further, I can't remember.

@cptpiepmatz
Copy link
Copy Markdown
Member

I'll take a look at today

Copy link
Copy Markdown
Member

@cptpiepmatz cptpiepmatz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation looks great, thank you!

@fdncred
Copy link
Copy Markdown
Contributor

fdncred commented Nov 20, 2025

wow, high praise. @KaiSforza was there anything else you wanted to do or are you ready to land this?

@KaiSforza
Copy link
Copy Markdown
Contributor Author

I think it's good as a first pass!

There's a few errors that are only going to be encountered with the new options (spans from "["|from nuon being shown for the current line, not the overall span), but those are going to be edge cases. I'm working on some solutions for that at the moment, this should just be an improvement for now!

@fdncred fdncred merged commit 949b474 into nushell:main Nov 20, 2025
16 checks passed
@fdncred
Copy link
Copy Markdown
Contributor

fdncred commented Nov 20, 2025

Thanks

@github-actions github-actions bot added this to the v0.109.0 milestone Nov 20, 2025
@WindSoilder WindSoilder added the notes:additions Include the release notes summary in the "Additions" section label Nov 24, 2025
@KaiSforza KaiSforza deleted the kai/error-more branch November 28, 2025 22:59
@KaiSforza KaiSforza mentioned this pull request Nov 28, 2025
1 task
fdncred pushed a commit that referenced this pull request Dec 18, 2025
## 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:🐚: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:🐚:error

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

Error: nu:🐚: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:🐚:error

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

Error: nu:🐚: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:🐚: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:🐚: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 `impl`s 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
<!-- Remove any tasks which aren't relevant for your PR, or add your own
-->
- [ ] Update the
[documentation](https://github.com/nushell/nushell.github.io)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

notes:additions Include the release notes summary in the "Additions" section

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants