jackwener/OpenCLI · error · ArgumentError

npm downloads period start ${start} is after end ${end}

Error message

npm downloads period start ${start} is after end ${end}

What it means

npm downloads accepts a period of a fixed keyword (last-day/week/month/year) or a YYYY-MM-DD:YYYY-MM-DD range. requirePeriod parses the value; when the range's start date is chronologically after its end date it throws ArgumentError with both dates interpolated. This is pure input validation done before any API call.

Source

Thrown at clis/npm/downloads.js:20

//
// Hits `api.npmjs.org/downloads/range/<period>/<pkg>`. Default window is the
// last 7 days (one row per day). Use `--period last-month` for 30 days, or
// pass a custom `YYYY-MM-DD:YYYY-MM-DD` range.
import { cli, Strategy } from '@jackwener/opencli/registry';
import { ArgumentError, EmptyResultError } from '@jackwener/opencli/errors';
import { NPM_API, npmFetch, requirePackageName } from './utils.js';

const FIXED_PERIODS = new Set(['last-day', 'last-week', 'last-month', 'last-year']);
const RANGE_PATTERN = /^(\d{4}-\d{2}-\d{2}):(\d{4}-\d{2}-\d{2})$/;

function requirePeriod(value) {
    const s = String(value ?? 'last-week').trim();
    if (FIXED_PERIODS.has(s)) return s;
    const m = RANGE_PATTERN.exec(s);
    if (m) {
        const [, start, end] = m;
        if (new Date(start) > new Date(end)) {
            throw new ArgumentError(`npm downloads period start ${start} is after end ${end}`);
        }
        return `${start}:${end}`;
    }
    throw new ArgumentError(
        `npm downloads period "${value}" is invalid`,
        'Use last-day / last-week (default) / last-month / last-year, or YYYY-MM-DD:YYYY-MM-DD.',
    );
}

cli({
    site: 'npm',
    name: 'downloads',
    access: 'read',
    description: 'Daily download counts for an npm package over a window',
    domain: 'api.npmjs.org',
    strategy: Strategy.PUBLIC,
    browser: false,
    args: [

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Swap the two dates so the earlier date comes first: period=YYYY-MM-DD:YYYY-MM-DD with start <= end
  2. If the range is computed, sort or assert start <= end before invoking the CLI
  3. Use a fixed period (last-week, last-month) when an exact range is not required

Example fix

// before
const period = `${to}:${from}`;
// after
const period = new Date(from) <= new Date(to) ? `${from}:${to}` : `${to}:${from}`;
Defensive patterns

Strategy: validation

Validate before calling

function validPeriod(from, to){
  const a = new Date(from), b = new Date(to);
  if (isNaN(a) || isNaN(b)) throw new Error('invalid date');
  if (a > b) throw new Error('start after end');
  return `${from}:${to}`;
}

Type guard

function isOrderedDateRange(p){ const m = /^(\d{4}-\d{2}-\d{2}):(\d{4}-\d{2}-\d{2})$/.exec(p); return !!m && new Date(m[1]) <= new Date(m[2]); }

Try / catch

try { await run(['npm-downloads', '--period', period]); }
catch (e) {
  if (e instanceof ArgumentError && /is after end/.test(e.message)) {
    const [a, b] = period.split(':');
    return run(['npm-downloads', '--period', `${b}:${a}`]); // swap and retry
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling npm downloads with a period like 2024-05-01:2024-01-01 (start after end), swapping the dates, or constructing the range programmatically with variables in the wrong order.

Common situations: Scripted date ranges computed as [from, to] but passed reversed; copy-paste of a range where someone edited only one date; timezone/mental-model mistakes about which date is earlier.

Related errors


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