jackwener/OpenCLI · error · ArgumentError

${label} must be a positive integer

Error message

${label} must be a positive integer

What it means

normalizePositiveInt coerces its input with Number() and requires the result to be an integer strictly greater than 0 (optionally capped by max). This ArgumentError is thrown when the caller passes a value that is missing-and-has-no-default, non-numeric, fractional, negative, or zero for an option labeled by `label` (e.g. adults, rooms, limit). It is an input-validation guard so the booking search CLI never runs with nonsensical counts.

Source

Thrown at clis/booking/search.js:14

import {
  ArgumentError,
  CommandExecutionError,
  EmptyResultError,
} from '@jackwener/opencli/errors';
import { cli, Strategy } from '@jackwener/opencli/registry';

const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;

function normalizePositiveInt(value, defaultValue, label, max) {
  const raw = value ?? defaultValue;
  const n = Number(raw);
  if (!Number.isInteger(n) || n <= 0) {
    throw new ArgumentError(`${label} must be a positive integer`);
  }
  if (typeof max === 'number' && n > max) {
    throw new ArgumentError(`${label} must be <= ${max}`);
  }
  return n;
}

function normalizeNonNegativeInt(value, defaultValue, label, max) {
  const raw = value ?? defaultValue;
  const n = Number(raw);
  if (!Number.isInteger(n) || n < 0) {
    throw new ArgumentError(`${label} must be a non-negative integer`);
  }
  if (typeof max === 'number' && n > max) {
    throw new ArgumentError(`${label} must be <= ${max}`);
  }
  return n;
}

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Pass a positive integer (>= 1) for the flagged option, e.g. --adults 2.
  2. If the value comes from user input, parse and validate it first: Number.parseInt + Number.isInteger + n > 0.
  3. Omit the option entirely so the built-in defaultValue applies (value ?? defaultValue), instead of explicitly passing null/0.
  4. Check the error message's `label` to identify exactly which option was invalid and re-run only fixing that one.

Example fix

// before
await bookingSearch({ adults: 0, rooms: 1 });
// throws: adults must be a positive integer
// after
await bookingSearch({ adults: 2, rooms: 1 });
Defensive patterns

Strategy: validation

Validate before calling

function assertPositiveInt(value, label, max) {
  const n = Number(value);
  if (!Number.isInteger(n) || n <= 0) throw new Error(`${label} must be a positive integer`);
  if (typeof max === 'number' && n > max) throw new Error(`${label} must be <= ${max}`);
  return n;
}
const adults = assertPositiveInt(opts.adults ?? 2, 'adults', 16);

Type guard

function isPositiveInt(v) {
  return typeof v === 'number' && Number.isInteger(v) && v > 0;
}

Try / catch

try {
  await bookingSearch({ adults, rooms, limit });
} catch (e) {
  if (e instanceof ArgumentError && e.message.includes('must be a positive integer')) {
    console.error(`Bad numeric option: ${e.message}`); process.exitCode = 2;
  } else throw e;
}

Prevention

When it happens

Trigger: Calling the search command with adults/rooms/limit set to values like 0, -1, 'two', 2.5, '', NaN, or null when no defaultValue applies. Also passing numeric strings with whitespace/units like '2 people', since Number('2 people') is NaN.

Common situations: Users typing `--adults 0` or `--limit -5`; config files exporting strings like 'undefined'; programmatically passing a variable that is undefined where the option had no default; parsing user input without trimming; locale-formatted numbers ('2,5').

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/6db57b51861f78cf. Report an issue: GitHub.