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
- Swap the two dates so the earlier date comes first: period=YYYY-MM-DD:YYYY-MM-DD with start <= end
- If the range is computed, sort or assert start <= end before invoking the CLI
- 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
- Always build ranges as earlierDate:laterDate
- Sort or assert start <= end when dates come from variables
- Prefer fixed keywords (last-week, last-month) when an exact range is unnecessary
- Use ISO YYYY-MM-DD formatting everywhere
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
- huodongxing date must be <= dateTo
- npm downloads period "${value}" is invalid
- weibo user-posts start must be <= end
- <train-no> must not be empty
- <train-no> "${trainNo}" does not look like a 12306 internal
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/c11815dc7e26244b.
Report an issue: GitHub.