tj/commander.js · error · Error

only the last argument can be variadic '${previousArgument.n

Error message

only the last argument can be variadic '${previousArgument.name()}'

What it means

Thrown by Command.addArgument() at lib/command.js:378-382 when an argument is added after an argument that is already variadic (name ending in `...`). Variadic arguments capture all remaining positional tokens, so nothing may follow them; Commander enforces this at registration time.

Source

Thrown at lib/command.js:379

    names
      .trim()
      .split(/ +/)
      .forEach((detail) => {
        this.argument(detail);
      });
    return this;
  }

  /**
   * Define argument syntax for command, adding a prepared argument.
   *
   * @param {Argument} argument
   * @return {Command} `this` command for chaining
   */
  addArgument(argument) {
    const previousArgument = this.registeredArguments.slice(-1)[0];
    if (previousArgument?.variadic) {
      throw new Error(
        `only the last argument can be variadic '${previousArgument.name()}'`,
      );
    }
    if (
      argument.required &&
      argument.defaultValue !== undefined &&
      argument.parseArg === undefined
    ) {
      throw new Error(
        `a default value for a required argument is never used: '${argument.name()}'`,
      );
    }
    this.registeredArguments.push(argument);
    return this;
  }

  /**
   * Customise or override default help command. By default a help command is automatically added if your command has subcommands.

View on GitHub (pinned to ba6d13ddb4)

Solutions

  1. Move the variadic to the end: `.arguments('<dest> <files...>')` is wrong order semantically — instead restructure to `.argument('<output>').argument('<inputs...>')` so variadic is last.
  2. If you need 'sources... then dest', require dest as an option (`.option('-o, --out <path>')`) instead of a positional, then keep `<files...>` as the sole variadic positional.
  3. Split into two commands or use a delimiter the user passes.

Example fix

// before (throws: variadic not last)
.arguments('<files...> <dest>')

// after (variadic last, dest as option)
.argument('<files...>')
.option('-o, --out <dest>', 'destination')
Defensive patterns

Strategy: validation

Validate before calling

// Ensure variadic is last when building args dynamically
function addArgsSafe(cmd, specs) {
  const lastVariadicIdx = specs.findIndex(s => s.endsWith('...'));
  if (lastVariadicIdx !== -1 && lastVariadicIdx !== specs.length - 1) {
    throw new Error('variadic argument must be the last one');
  }
  specs.forEach(s => cmd.argument(s));
}

Prevention

When it happens

Trigger: `.arguments('<files...> <dest>')`, or chained `.argument('<inputs...>').argument('<output>')`. The check looks at the last registered argument: if it is variadic, adding another throws.

Common situations: Trying to express 'many source files then a destination' with the variadic in the wrong slot; converting a fixed-arity signature to variadic and forgetting to reorder; misreading docs that show variadic must be last.

Related errors


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