util.parseArgs()

Parses command-line arguments into named values and positionals from a declared option spec.

Signature
util.parseArgs([config])
  • Added in Node.js v18.3.0, v16.17.0

Parameters

ParameterTypeDescription
$config.argsoptionalstring[]= process.argv with execPath and filename removedArray of argument strings to parse.
$config.optionsoptionalObject= {}Describes the options known to the parser. Each key is a long option name; each value takes type, multiple, short and default.
$config.options[name].typestring'boolean' or 'string'. Required for every declared option.
$config.options[name].multipleoptionalboolean= falseWhen true all occurrences are collected into an array; when false the last occurrence wins.
$config.options[name].shortoptionalstringA single-character alias for the option.
$config.options[name].defaultoptionalstring | boolean | string[] | boolean[]Value assigned when the option does not appear in args. Must match `type`, and must be an array when `multiple` is true.
$config.strictoptionalboolean= trueThrow when an unknown argument is seen or when a value does not match the configured type.
$config.allowPositionalsoptionalboolean= false if strict is true, otherwise trueWhether the command accepts positional arguments.
$config.allowNegativeoptionalboolean= falseAllow boolean options to be set to false with a --no- prefix.
$config.tokensoptionalboolean= falseAlso return the parsed tokens, for extending the built-in behaviour.

Return value

ObjectAn object with `values` (a null-prototype map of parsed options), `positionals` (an array of strings) and, when `config.tokens` is true, `tokens`.

Errors

In strict mode throws ERR_PARSE_ARGS_UNKNOWN_OPTION for an undeclared option, ERR_PARSE_ARGS_INVALID_OPTION_VALUE for a type mismatch, and ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL when positionals are not allowed.

Changelog

  • PHP v22.4.0, v20.16.0Added allowNegative, so --no-color can set a boolean option to false.
  • PHP v20.0.0The API is no longer experimental.
  • PHP v18.11.0, v16.19.0Added support for default values in the options config.
  • PHP v18.7.0, v16.17.0Added tokens: true for detailed parse information.

util.parseArgs() turns process.argv into a structured result without a dependency. You declare the options you accept, it returns values and positionals.

When to reach for it

Reach for it in scripts and small CLIs — build tooling, migrations, internal utilities — where adding yargs or commander is more machinery than the job needs. Note the boundary: parseArgs() parses. It does not generate help text, define subcommands, coerce numbers, or validate that a required option was supplied. Those are yours to write, and once you need several of them a full framework is the better trade.

What trips people up

  • Everything is a string or a boolean. --port 8080 gives you '8080'; there is no type: 'number'. Convert and range-check yourself.
  • Positionals are off by default in strict mode — a bare filename throws ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL until you set allowPositionals: true.
  • Unknown options throw. That is usually right for a CLI, but it means you must declare every flag, including --help.
  • values has a null prototype, so values.hasOwnProperty(...) is not a function. Use Object.hasOwn() or spread it into a plain object.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Add --dry-run and --out flags to a maintenance script without pulling in a CLI framework.
  • Parse an argument array you built yourself, such as arguments forwarded from another tool.
  • Implement mutually exclusive flags where the last one on the command line wins, using tokens.

Frequently asked questions

How do I parse a number option with util.parseArgs?

You cannot directly — `type` accepts only `'string'` and `'boolean'`. Declare the option as `'string'` and convert afterwards with `Number(values.port)`, checking `Number.isInteger` or `Number.isFinite` yourself, because `parseArgs` performs no coercion or range validation.

Why does util.parseArgs throw ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL?

Because `allowPositionals` defaults to `false` whenever `strict` is `true`, so a bare filename or command name is rejected. Set `allowPositionals: true` in the config and the argument appears in the returned `positionals` array.

Is util.parseArgs a replacement for yargs or commander?

For single-command scripts, yes — it handles long and short flags, repeated values, defaults and `--no-` negation with no dependency. It does not provide help text generation, subcommands, type coercion or required-option validation, so a CLI that needs several of those is still better served by a framework.

More examples

A real CLI entry point with help and validation

This is the shape most scripts end up with. Three things `parseArgs` does not do are all visible: `--help` has to be declared and handled like any other flag, `--port` arrives as the string `'3000'` and needs `Number()` plus a range check, and the throw on an unknown option must be caught if you want a clean message instead of a stack trace. `allowNegative: true` is what makes `--no-color` set `color` to `false`.

Example · javascript
#!/usr/bin/env node
const util = require('node:util');

const options = {
  help: { type: 'boolean', short: 'h', default: false },
  port: { type: 'string', short: 'p', default: '3000' },
  color: { type: 'boolean', default: true },
};

let values;
let positionals;
try {
  // Defaults to process.argv minus execPath and filename.
  ({ values, positionals } = util.parseArgs({
    options,
    allowPositionals: true,
    allowNegative: true, // enables --no-color
  }));
} catch (err) {
  console.error(err.message); // e.g. Unknown option '--prot'. Did you mean '--port'?
  process.exit(2);
}

if (values.help) {
  console.log('usage: serve [--port N] [--no-color] [dir]');
  process.exit(0);
}

// parseArgs never coerces types — do it yourself.
const port = Number(values.port);
if (!Number.isInteger(port) || port < 1 || port > 65535) {
  console.error(`invalid --port: ${values.port}`);
  process.exit(2);
}

console.log({ port, color: values.color, dir: positionals[0] ?? '.' });

Strict mode, and the errors it produces

Each failure has its own `err.code`, so a CLI can distinguish a typo from a missing value and respond differently. The last line shows the loose mode: with `strict: false`, `allowPositionals` flips to `true`, unknown flags are accepted as booleans, and nothing throws — useful for a wrapper that forwards unrecognised arguments to another command.

Example · javascript
const util = require('node:util');

const options = { name: { type: 'string' }, force: { type: 'boolean' } };

const attempts = [
  ['--nope'],            // undeclared option
  ['file.txt'],          // positional, not allowed by default
  ['--force=yes'],       // value given to a boolean
  ['--name'],            // string option with no value
];

for (const args of attempts) {
  try {
    util.parseArgs({ args, options });
    console.log(args, '-> ok');
  } catch (err) {
    console.log(args, '->', err.code);
  }
}
// [ '--nope' ] -> ERR_PARSE_ARGS_UNKNOWN_OPTION
// [ 'file.txt' ] -> ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL
// [ '--force=yes' ] -> ERR_PARSE_ARGS_INVALID_OPTION_VALUE
// [ '--name' ] -> ERR_PARSE_ARGS_INVALID_OPTION_VALUE

// strict: false collects everything instead of throwing.
console.log(util.parseArgs({ args: ['--nope', 'file.txt'], options, strict: false }));
// { values: [Object: null prototype] { nope: true }, positionals: [ 'file.txt' ] }

Using tokens to implement last-flag-wins

`values` records only the final state, so it cannot tell you that `--verbose` came after `--quiet`. `tokens: true` returns the arguments in order with `index`, `rawName` (the short or long form actually typed) and `inlineValue` (whether the value came from `--a=1` or a separate argument), which is the hook for order-sensitive behaviour like mutually exclusive flags, counting `-vvv`, or stopping at a subcommand name.

Example · javascript
const util = require('node:util');

// --verbose and --quiet are opposites; whichever appears last should win.
const { values, tokens } = util.parseArgs({
  args: ['--verbose', '--quiet', '--verbose'],
  options: {
    verbose: { type: 'boolean' },
    quiet: { type: 'boolean' },
  },
  tokens: true,
});

let level = 'normal';
for (const token of tokens) {
  if (token.kind !== 'option') continue;
  if (token.name === 'verbose') level = 'verbose';
  if (token.name === 'quiet') level = 'quiet';
}

console.log('values:', { ...values }); // { verbose: true, quiet: true }
console.log('level:', level);          // verbose

// Tokens carry the raw form and position, which values cannot.
console.log(util.parseArgs({
  args: ['--a=1', 'x'],
  options: { a: { type: 'string' } },
  allowPositionals: true,
  tokens: true,
}).tokens);
// [
//   { kind: 'option', name: 'a', rawName: '--a', index: 0,
//     value: '1', inlineValue: true },
//   { kind: 'positional', index: 1, value: 'x' }
// ]

Discussion

  • Be the first to comment on this lesson.