jackwener/OpenCLI · error · ArgumentError

${label} must be a positive integer

Error message

${label} must be a positive integer

What it means

`hackernews read` validates its numeric CLI options through `requirePositiveInt`, which throws `ArgumentError` when a value is not an integer or is <= 0. This guards `--limit`, `--depth`, and `--replies` before any network call is made, so bad input fails fast with a message naming the exact flag. The library throws it because these options control pagination/recursion counts where zero or negative values are meaningless.

Source

Thrown at clis/hackernews/read.js:28

 *   - each subsequent row is a comment, indented by depth (`L0`, `L1`, …)
 *   - `[+N more replies]` summary rows whenever depth/limit cuts in
 */
import { cli, Strategy } from '@jackwener/opencli/registry';
import { ArgumentError, CommandExecutionError, EmptyResultError } from '@jackwener/opencli/errors';

const HN_ITEM_BASE = 'https://hacker-news.firebaseio.com/v0/item';

async function fetchItem(id) {
    const res = await fetch(`${HN_ITEM_BASE}/${id}.json`);
    if (!res.ok) {
        throw new CommandExecutionError(`HN API HTTP ${res.status} for item ${id}`, 'Check the item ID');
    }
    return res.json();
}

function requirePositiveInt(value, label) {
    if (!Number.isInteger(value) || value <= 0) {
        throw new ArgumentError(`${label} must be a positive integer`);
    }
    return value;
}

function requireMinInt(value, min, label) {
    if (!Number.isInteger(value) || value < min) {
        throw new ArgumentError(`${label} must be an integer >= ${min}`);
    }
    return value;
}

/** HN stores comment text as a small HTML subset — convert to plain text. */
function htmlToText(html) {
    if (!html) return '';
    return String(html)
        .replace(/<p>/gi, '\n\n')
        .replace(/<\/p>/gi, '')
        .replace(/<br\s*\/?>/gi, '\n')

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Check the failing flag named in the message (e.g. `hackernews read --limit`) and set it to a positive integer (1, 2, 3, ...)
  2. Remove the flag entirely to use the default (`--limit 25`, `--depth 2`, `--replies 5`)
  3. If the value comes from a shell variable or script, verify it is non-empty and numeric before invoking the CLI

Example fix

// before
opencli hackernews read 39847301 --limit 0
// after
opencli hackernews read 39847301 --limit 25
Defensive patterns

Strategy: validation

Validate before calling

const LIMIT_RE = /^\d+$/;
function assertPositiveInt(v) {
  if (!Number.isInteger(Number(v)) || Number(v) <= 0 || !LIMIT_RE.test(String(v))) {
    throw new Error(`--limit must be a positive integer, got: ${v}`);
  }
}
assertPositiveInt(process.env.HN_LIMIT ?? 25);

Type guard

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

Try / catch

try {
  await run(['opencli', 'hackernews', read, id, '--limit', String(limit)]);
} catch (e) {
  if (String(e.message).includes('must be a positive integer')) {
    console.error(`Bad --limit value: ${limit}; using default 25`);
    await run(['opencli', 'hackernews', 'read', id]);
  } else throw e;
}

Prevention

When it happens

Trigger: Running `opencli hackernews read <id>` with `--limit 0`, `--limit -5`, `--depth 0`, `--replies 0`, or a non-integer like `--limit 2.5` or `--limit abc`. Passing a numeric string via shell variable interpolation that the CLI parser does not coerce to int also triggers it.

Common situations: Scripted invocations where limit/depth come from computed shell variables that end up 0 or empty; users assuming 0 means 'unlimited'; typos like `--limit '' ` from an unset env var; pasting float values copied from configs.

Understand the failure class

Background: "Unknown argument", "Invalid value", and "must be one of": invalid CLI argument errors explained — this error's family across 35 libraries.

Related errors


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