jackwener/OpenCLI · error · EmptyResultError
`endoflife.date returned no cycles for "${product}".`
Error message
`endoflife.date returned no cycles for "${product}".` What it means
The endoflife product CLI fetches https://endoflife.date/<product>.json and throws an EmptyResultError when the response is not a non-empty array of release cycles. This means the API responded but contained no cycle data for the given product slug.
Source
Thrown at clis/endoflife/product.js:30
site: 'endoflife',
name: 'product',
access: 'read',
description: 'Release cycles + EOL / LTS / support dates for one product on endoflife.date',
domain: 'endoflife.date',
strategy: Strategy.PUBLIC,
browser: false,
args: [
{ name: 'product', positional: true, type: 'string', required: true, help: 'endoflife.date product slug (e.g. "nodejs", "python", "ubuntu")' },
],
columns: [
'product', 'cycle', 'releaseDate', 'latest', 'latestReleaseDate',
'lts', 'support', 'eol', 'extendedSupport', 'eolStatus', 'url',
],
func: async (args) => {
const product = requireProduct(args.product);
const cycles = await eolFetch(`${EOL_BASE}/${encodeURIComponent(product)}.json`, `endoflife product ${product}`);
if (!Array.isArray(cycles) || cycles.length === 0) {
throw new EmptyResultError('endoflife product', `endoflife.date returned no cycles for "${product}".`);
}
const today = new Date().toISOString().slice(0, 10);
return cycles.map((c) => {
const eol = normaliseDateOrFlag(c?.eol);
// eolStatus projection — best-effort, derived from eol vs today (not from a remote field).
let eolStatus = null;
if (eol === 'ongoing') eolStatus = 'ongoing';
else if (typeof eol === 'string' && eol >= today) eolStatus = 'active';
else if (typeof eol === 'string') eolStatus = 'eol';
return {
product,
cycle: String(c?.cycle ?? '').trim(),
releaseDate: typeof c?.releaseDate === 'string' ? c.releaseDate : null,
latest: String(c?.latest ?? '').trim(),
latestReleaseDate: typeof c?.latestReleaseDate === 'string' ? c.latestReleaseDate : null,
lts: normaliseDateOrFlag(c?.lts),
support: normaliseDateOrFlag(c?.support),
eol,View on GitHub (pinned to 49907e53dc)
Solutions
- Verify the product slug exists at https://endoflife.date/<product> in a browser
- Fix the slug spelling (e.g. 'nodejs' not 'node', 'python' not 'python3')
- Run requireProduct validation on the input before the fetch
- Handle EmptyResultError by falling back to a cached/alternative data source
Example fix
// before
product('node'); // may yield no cycles
// after
product('nodejs'); Defensive patterns
Strategy: try-catch
Validate before calling
const slug = String(product ?? '').trim().toLowerCase();
if (!/^[a-z0-9][a-z0-9._-]{0,79}$/.test(slug)) throw new Error(`invalid endoflife slug: ${product}`); Type guard
function isValidEolSlug(v) { return typeof v === 'string' && /^[a-z0-9][a-z0-9._-]{0,79}$/.test(v.trim().toLowerCase()); } Try / catch
try {
const cycles = await eolProduct('nodejs');
} catch (e) {
if (e instanceof EmptyResultError) console.warn(`no cycles for product: ${e.message}`);
else throw e;
} Prevention
- Verify the slug exists on endoflife.date before querying
- Use official slugs (nodejs, python, ubuntu), not display names
- Cache cycle data and fall back to it when a product returns empty
- Watch for endoflife.date renames/removals of products
When it happens
Trigger: Fetching a product slug that exists loosely (or redirects) but has no cycles JSON, an invalid slug where endoflife.date returns an empty/non-array body instead of 404, or an API schema change.
Common situations: Typo'd product slug that happens to resolve, product removed or renamed on endoflife.date, endpoint returning an empty array during partial outages or maintenance.
Related errors
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/04f2e160ea0e4c26.
Report an issue: GitHub.