tj/commander.js · error · Error

Unexpected value for event passed to hook : '${event}'. Expe

Error message

Unexpected value for event passed to hook : '${event}'.
Expecting one of '${allowedValues.join("', '")}'

What it means

Thrown by Command.hook() at lib/command.js:490-493 when the `event` argument is not one of the supported lifecycle events: 'preSubcommand', 'preAction', or 'postAction'. Hooks are stored keyed by event in _lifeCycleHooks, so an unknown event would silently never fire — Commander fails fast instead.

Source

Thrown at lib/command.js:491

        this.helpCommand(undefined, undefined); // use default name and description
      }
      return this._helpCommand;
    }
    return null;
  }

  /**
   * Add hook for life cycle event.
   *
   * @param {string} event
   * @param {Function} listener
   * @return {Command} `this` command for chaining
   */

  hook(event, listener) {
    const allowedValues = ['preSubcommand', 'preAction', 'postAction'];
    if (!allowedValues.includes(event)) {
      throw new Error(`Unexpected value for event passed to hook : '${event}'.
Expecting one of '${allowedValues.join("', '")}'`);
    }
    if (this._lifeCycleHooks[event]) {
      this._lifeCycleHooks[event].push(listener);
    } else {
      this._lifeCycleHooks[event] = [listener];
    }
    return this;
  }

  /**
   * Register callback to use as replacement for calling process.exit.
   *
   * @param {Function} [fn] optional callback which will be passed a CommanderError, defaults to throwing
   * @return {Command} `this` command for chaining
   */

  exitOverride(fn) {

View on GitHub (pinned to ba6d13ddb4)

Solutions

  1. Use one of the documented events exactly: 'preSubcommand', 'preAction', or 'postAction' (case-sensitive).
  2. Upgrade/downgrade to align Commander version with the event names your code assumes.
  3. If you need a phase Commander doesn't expose (e.g. pre-parse), use the action handler itself or wrap program.parse.

Example fix

// before (throws)
.hook('preParse', (cmd, opts) => { /* ... */ })

// after
.hook('preAction', (cmd, opts) => { /* ... */ })
Defensive patterns

Strategy: type-guard

Validate before calling

const HOOK_EVENTS = ['preSubcommand', 'preAction', 'postAction'];
function registerHook(cmd, event, fn) {
  if (!HOOK_EVENTS.includes(event)) {
    throw new TypeError(`hook event must be one of ${HOOK_EVENTS.join(', ')}`);
  }
  cmd.hook(event, fn);
}

Type guard

type HookEvent = 'preSubcommand' | 'preAction' | 'postAction';
function isHookEvent(v: unknown): v is HookEvent {
  return v === 'preSubcommand' || v === 'preAction' || v === 'postAction';
}

Prevention

When it happens

Trigger: Calling `.hook('preParse', fn)`, `.hook('beforeAction', fn)`, or any string outside the allow-list. Typos like 'preaction' (lowercase) vs 'preAction' (camelCase) also trip it because the check is case-sensitive.

Common situations: Version skew: an older Commander version had fewer hook events and code was written against different names; copy-pasting hook names from a blog post that used non-existent events; assuming symmetric 'pre'/'post' naming for every phase.

Related errors


AI-assisted analysis of tj/commander.js@ba6d13ddb4 (2026-08-03). Data as JSON: /data/errors/8c21482278aa5b5b.json. Report an issue: GitHub.