Inquirer.js icon indicating copy to clipboard operation
Inquirer.js copied to clipboard

If no value is provided list prompt fails with TypeError: Cannot read property 'value' of undefined

Open wwhurley opened this issue 8 years ago • 6 comments

The full stack trace is

TypeError: Cannot read property 'value' of undefined
    at Prompt.getCurrentValue (/opt/local/lib/node_modules/yo/node_modules/yeoman-environment/node_modules/inquirer/lib/prompts/list.js:123:51)
    at tryCatcher (/opt/local/lib/node_modules/yo/node_modules/rx/dist/rx.js:63:31)
    at InnerObserver.next (/opt/local/lib/node_modules/yo/node_modules/rx/dist/rx.js:5407:43)
    at InnerObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (/opt/local/lib/node_modules/yo/node_modules/rx/dist/rx.js:1762:31)
    at InnerObserver.tryCatcher (/opt/local/lib/node_modules/yo/node_modules/rx/dist/rx.js:63:31)
    at AutoDetachObserverPrototype.next (/opt/local/lib/node_modules/yo/node_modules/rx/dist/rx.js:5883:51)
    at AutoDetachObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (/opt/local/lib/node_modules/yo/node_modules/rx/dist/rx.js:1762:31)
    at TakeObserver.next (/opt/local/lib/node_modules/yo/node_modules/rx/dist/rx.js:5646:17)
    at TakeObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (/opt/local/lib/node_modules/yo/node_modules/rx/dist/rx.js:1762:31)
    at TakeObserver.tryCatcher (/opt/local/lib/node_modules/yo/node_modules/rx/dist/rx.js:63:31)
    at AutoDetachObserverPrototype.next (/opt/local/lib/node_modules/yo/node_modules/rx/dist/rx.js:5883:51)
    at AutoDetachObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (/opt/local/lib/node_modules/yo/node_modules/rx/dist/rx.js:1762:31)
    at Subject.Rx.Subject.addProperties.onNext (/opt/local/lib/node_modules/yo/node_modules/rx/dist/rx.js:5998:19)
    at Subject.tryCatcher (/opt/local/lib/node_modules/yo/node_modules/rx/dist/rx.js:63:31)
    at AutoDetachObserverPrototype.next (/opt/local/lib/node_modules/yo/node_modules/rx/dist/rx.js:5883:51)
    at AutoDetachObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (/opt/local/lib/node_modules/yo/node_modules/rx/dist/rx.js:1762:31)
    at AutoDetachObserver.tryCatcher (/opt/local/lib/node_modules/yo/node_modules/rx/dist/rx.js:63:31)
    at AutoDetachObserverPrototype.next (/opt/local/lib/node_modules/yo/node_modules/rx/dist/rx.js:5883:51)
    at AutoDetachObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (/opt/local/lib/node_modules/yo/node_modules/rx/dist/rx.js:1762:31)
    at Interface.handler (/opt/local/lib/node_modules/yo/node_modules/rx/dist/rx.async.js:482:11)
    at emitOne (events.js:77:13)
    at Interface.emit (events.js:169:7)
    at Interface._onLine (readline.js:211:10)
    at Interface._line (readline.js:550:8)
    at Interface._ttyWrite (readline.js:827:14)
    at ReadStream.onkeypress (readline.js:106:10)
    at emitTwo (events.js:92:20)
    at ReadStream.emit (events.js:172:7)
    at emitKeys (readline.js:1251:14)
    at next (native)
    at ReadStream.onData (readline.js:919:36)
    at emitOne (events.js:77:13)
    at ReadStream.emit (events.js:169:7)
    at readableAddChunk (_stream_readable.js:153:18)
    at ReadStream.Readable.push (_stream_readable.js:111:10)
    at TTY.onread (net.js:531:20)

The code that causes this is

return that.prompt([{
  // Put config prompts here
  type : 'list',
  name : 'base',
  choices : versions,
  message : 'Select a version',
  default : config.base,
}])

The exception occurs immediately after hitting enter without selecting a value. I attempted to add a validate function to catch the empty value but the exception occurred before it was called.

wwhurley avatar Sep 22 '16 02:09 wwhurley

what is versions here? Any chance one of the value in the array is undefined?

SBoudrias avatar Sep 22 '16 07:09 SBoudrias

So it turns out that the code I was reviewing set the default value to the label of the option not the value of the option. So this particular case was simple developer error. Though it does leave open the fact that the developer needs to verify that the default value exists within the choices otherwise you get an exception. That's a different problem from what I described, but I think the hard error is somewhat problematic.

wwhurley avatar Sep 22 '16 16:09 wwhurley

Just found in v3.3.0

readline.js:981
            throw err;
            ^

TypeError: Cannot read property 'value' of undefined

Will see if I can mitigate with a default value per @wwhurley

eephillip avatar Sep 19 '17 15:09 eephillip

This is not exactly relevant but perhaps it could be a part of a wider improvement to handle null and undefined's for better dev experience, which could be separated out into an individual issue.

I think it's not unreasonable for the Choices object to handle false, null, undefined within the array, in a reasonable way.

How I'd like to use it (made up):

const choices = [
    { key: 's', name: "Skip", value: 'skip' },
    condition ? { key: 'c', name: 'Continue', value: 'continue' } : null, // <- this
    { key: 's', name: "Stop", value: 'stop' }
]

Error call stack (trimmed):

TypeError: Cannot read property 'type' of null
    at Choices.choices.choices.map.val (<project>/node_modules/inquirer/lib/objects/choices.js:17:15)
    at Array.map (<anonymous>)
    at new Choices (<project>/node_modules/inquirer/lib/objects/choices.js:16:28)
    ...

Currently I push to the choices array, and if I want it to appear in a specific location I'd need to reconstruct the array somehow, meh.

Unless there's a different approach of how to do this within Inquirer.

Thanks 🙂

dddom avatar Apr 17 '18 08:04 dddom

@dddom you can just do choices.filter(Boolean) to remove the falsy value from your array.

SBoudrias avatar Apr 18 '18 04:04 SBoudrias

We recently got a similar error with the Amplify CLI, the root cause is that if you pass in the [] list of choices, then

  getCurrentValue() {
    return this.opt.choices.getChoice(this.selected).value;
  }

fails as getChoice returned undefined hence no items in the line so no selection, I think that something like this would fix it:

  getCurrentValue() {
    const selection = this.opt.choices.getChoice(this.selected);

    return selection ?? selection.value;
  }

On the other hand since inquirer is based on RXJS in the caller code it ends up as an UnhandledPromiesRejectionWarning, which is not the nicest thing and the stacktrace for the error has nothing to do with the actual call chain because of the nature of how EventEmitter fired events work:

(node:96356) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'value' of undefined
    at ListPrompt.getCurrentValue (/Users/attila/workspaces/amplify-cli/node_modules/inquirer/lib/prompts/list.js:148:53)
    at MapSubscriber._next (/Users/attila/workspaces/amplify-cli/node_modules/rxjs/internal/operators/map.js:49:35)
    at MapSubscriber.Subscriber.next (/Users/attila/workspaces/amplify-cli/node_modules/rxjs/internal/Subscriber.js:66:18)
    at TakeSubscriber._next (/Users/attila/workspaces/amplify-cli/node_modules/rxjs/internal/operators/take.js:54:30)
    at TakeSubscriber.Subscriber.next (/Users/attila/workspaces/amplify-cli/node_modules/rxjs/internal/Subscriber.js:66:18)
    at Interface.handler (/Users/attila/workspaces/amplify-cli/node_modules/rxjs/internal/observable/fromEvent.js:22:28)
    at Interface.emit (events.js:327:22)
    at Interface.EventEmitter.emit (domain.js:486:12)
    at Interface._onLine (readline.js:337:10)
    at Interface._line (readline.js:666:8)
    at Interface._ttyWrite (readline.js:1010:14)
    at ReadStream.onkeypress (readline.js:213:10)
    at ReadStream.emit (events.js:327:22)
    at ReadStream.EventEmitter.emit (domain.js:486:12)
    at emitKeys (internal/readline/utils.js:345:14)
    at emitKeys.next (<anonymous>)
    at ReadStream.onData (readline.js:1144:36)
    at ReadStream.emit (events.js:315:20)
    at ReadStream.EventEmitter.emit (domain.js:486:12)
    at addChunk (_stream_readable.js:309:12)
    at readableAddChunk (_stream_readable.js:284:9)
    at ReadStream.Readable.push (_stream_readable.js:223:10)
    at TTY.onStreamRead (internal/stream_base_commons.js:188:23)
    at TTY.callbackTrampoline (internal/async_hooks.js:129:14)

attilah avatar Feb 19 '21 23:02 attilah