nushell: Nushell thread panics if a variable is passed to a custom command that contains a recursive call to itself.

Describe the bug

In the REPL of Nushell, if you try to send any variable to a custom command that uses recursion to call itself, the Nushell executable will thread panic as soon as you type enough of the custom command’s name to make it disticnt. However, if you pass the exact same value as a literal it works normally. This does not happen if you embed the custom command inside a Nu script or if you call nu with using the ‘-c’ flag.

Attempting to fool Nu by disguising the variable in a direct call to a simple closure it will still panic. Also, if you create another custom command that refers to the global variable and returns it, the Nu reader still panics. Local variables that are then returned work normally. Only global variables that are passed to bad custom command cause Nu to panic.

How to reproduce

  1. Create a custom command that accepts input., and recursively calls itself with that input.
  2. Create a variable that contains some value of any type.
  • I used a simple int.
  1. In the REPL of Nu, try and type a pipeline passing the dereference of that variable to the custom command.
  2. As soon as you type enough of the command’s name that it is distinct, nu thread panics.

Here is a sample command that will trigger the panic:

# This command does nothing but countdown to 0 and returns false.
def px [n=0] {
  let l = $in
  if $n == 0 { return false } else {
    $l | px ($n - 1)
  }
}

Sammple session in the REPL


minerva issues.nushell >>  > source px.nu
minerva issues.nushell >>  > 1 | px 3
false
minerva issues.nushell >>  > let x = 1
minerva issues.nushell >>  > $x | pthread 'main' panicked at 'Attempt to mutate a block that is in the permanent (immutable) state', /Users/edhowland/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nu-protocol-0.85.0/src/engine/engine_state.rs:1789:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
$ 

… back in Bash.

Note the following working steps



mini issues.nushellnu -c "source px.nu; let x = 1; $x | px 3"
false

Or running from a .nu script:

# t-px.nu: Attempt to trigger the panic from a script
source px.nu

let x = 1
$x | px 3

Then run it:



These examples all work as intended.

This only triggers the panic with global variables.
Local variables in a block or  in another custom command or closure will not
trigger the panic.

```nu

minerva issues.nushell >>  > source px.nu
minerva issues.nushell >>  > if true {
::: let x = 1
::: $x
::: } | px 3
false
minerva issues.nushell >>  > do {|y| $y } 2 | px 3
false
minerva issues.nushell >>  > def foo [] {
::: let z = 1
::: $z
::: }
minerva issues.nushell >>  > foo | px 3

false



But, using a global variable in the above examples will trigger the panic.


```nu

minerva issues.nushell >>  > open foo.nu
source px.nu
let x = 1
def foo [] {
  $x
}
minerva issues.nushell >>  > source foo.nu
minerva issues.nushell >>  > foo | pthread 'main' panicked at 'Attempt to mutate a block that is in the permanent (immutable) state', /Users/edhowland/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nu-protocol-0.85.0/src/engine/engine_state.rs:1789:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Or with a simple closure: (also works with blocks:

minerva issues.nushell >>  > source px.nu
minerva issues.nushell >>  > let x = 1
minerva issues.nushell >>  > do {|| $x } | pthread 'main' panicked at 'Attempt to mutate a block that is in the permanent (immutable) state', /Users/edhowland/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nu-protocol-0.85.0/src/engine/engine_state.rs:1789:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Expected behavior

you should be able to write a custom command in Nu that takes input from a pipeline and uses recursion to perform its job. This does work properly in most cases if the input to the command is either a local variable, from some earlier command that fetches/opens/generates output to feed to the command. You should be able to experiment with that command within the command line environment and if you use a literal value it works ok. You should be bale to store that literal value in a global variable so you do not have to retype it every time and then just dereference the global variable and pass it to the pipeline input. This latter use of a global variable triggers the hard panic.

Screenshots

minerva issues.nushell >> > source px.nu minerva issues.nushell >> > let x = 1 minerva issues.nushell >> > $x | pthread ‘main’ panicked at ‘Attempt to mutate a block that is in the permanent (immutable) state’, /Users/edhowland/.cargo/registry/src/index.crates.io-6f17d22bba15001f/nu-protocol-0.85.0/src/engine/engine_state.rs:1789:13 note: run with RUST_BACKTRACE=1 environment variable to display a backtrace $

Configuration

key value
version 0.85.0
branch
commit_hash
build_os macos-aarch64
build_target aarch64-apple-darwin
rust_version rustc 1.72.0 (5680fa18f 2023-08-23)
rust_channel stable-aarch64-apple-darwin
cargo_version cargo 1.72.0 (103a7ff2e 2023-08-15)
build_time 2023-10-02 09:52:38 -04:00
build_rust_channel release
allocator mimalloc
features default, sqlite, trash, which, zip
installed_plugins query, query json, query web, query xml
key value
------------------ ---------------------------------------
version 0.85.0
branch
commit_hash
build_os macos-aarch64
build_target aarch64-apple-darwin
rust_version rustc 1.72.0 (5680fa18f 2023-08-23)
rust_channel stable-aarch64-apple-darwin
cargo_version cargo 1.72.0 (103a7ff2e 2023-08-15)
build_time 2023-10-02 09:52:38 -04:00
build_rust_channel release
allocator mimalloc
features default, sqlite, trash, which, zip
installed_plugins query, query json, query web, query xml

Additional context

Tried to get the backtrace as suggested by setting RUST_BACKTRACE=1 but I think that to do that I need to compile nu in debug mode first not in release mode.

About this issue

  • Original URL
  • State: closed
  • Created 6 months ago
  • Comments: 16 (7 by maintainers)

Commits related to this issue

Most upvoted comments

Tried to look into it, and found it doesn’t work with $in, panic can happened if we have a recursive call.

Here is a smaller reproducable example:

def px [] { if true { 3 } else { px } }
let x = 1
$x | px

When type final x in repl, nushell panic.