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

  1. Verify the product slug exists at https://endoflife.date/<product> in a browser
  2. Fix the slug spelling (e.g. 'nodejs' not 'node', 'python' not 'python3')
  3. Run requireProduct validation on the input before the fetch
  4. 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

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.