{"record":{"id":"04f2e160ea0e4c26","repo":"jackwener/OpenCLI","slug":"endoflife-date-returned-no-cycles-for-product","errorCode":null,"errorMessage":"`endoflife.date returned no cycles for \"${product}\".`","messagePattern":"`endoflife\\.date returned no cycles for \"(.+?)\"\\.`","errorType":"error_code","errorClass":"EmptyResultError","httpStatus":null,"severity":"error","filePath":"clis/endoflife/product.js","lineNumber":30,"sourceCode":"    site: 'endoflife',\n    name: 'product',\n    access: 'read',\n    description: 'Release cycles + EOL / LTS / support dates for one product on endoflife.date',\n    domain: 'endoflife.date',\n    strategy: Strategy.PUBLIC,\n    browser: false,\n    args: [\n        { name: 'product', positional: true, type: 'string', required: true, help: 'endoflife.date product slug (e.g. \"nodejs\", \"python\", \"ubuntu\")' },\n    ],\n    columns: [\n        'product', 'cycle', 'releaseDate', 'latest', 'latestReleaseDate',\n        'lts', 'support', 'eol', 'extendedSupport', 'eolStatus', 'url',\n    ],\n    func: async (args) => {\n        const product = requireProduct(args.product);\n        const cycles = await eolFetch(`${EOL_BASE}/${encodeURIComponent(product)}.json`, `endoflife product ${product}`);\n        if (!Array.isArray(cycles) || cycles.length === 0) {\n            throw new EmptyResultError('endoflife product', `endoflife.date returned no cycles for \"${product}\".`);\n        }\n        const today = new Date().toISOString().slice(0, 10);\n        return cycles.map((c) => {\n            const eol = normaliseDateOrFlag(c?.eol);\n            // eolStatus projection — best-effort, derived from eol vs today (not from a remote field).\n            let eolStatus = null;\n            if (eol === 'ongoing') eolStatus = 'ongoing';\n            else if (typeof eol === 'string' && eol >= today) eolStatus = 'active';\n            else if (typeof eol === 'string') eolStatus = 'eol';\n            return {\n                product,\n                cycle: String(c?.cycle ?? '').trim(),\n                releaseDate: typeof c?.releaseDate === 'string' ? c.releaseDate : null,\n                latest: String(c?.latest ?? '').trim(),\n                latestReleaseDate: typeof c?.latestReleaseDate === 'string' ? c.latestReleaseDate : null,\n                lts: normaliseDateOrFlag(c?.lts),\n                support: normaliseDateOrFlag(c?.support),\n                eol,","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/endoflife/product.js#L12-L48","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nproduct('node'); // may yield no cycles\n// after\nproduct('nodejs');","handlingStrategy":"try-catch","validationCode":"const slug = String(product ?? '').trim().toLowerCase();\nif (!/^[a-z0-9][a-z0-9._-]{0,79}$/.test(slug)) throw new Error(`invalid endoflife slug: ${product}`);","typeGuard":"function isValidEolSlug(v) { return typeof v === 'string' && /^[a-z0-9][a-z0-9._-]{0,79}$/.test(v.trim().toLowerCase()); }","tryCatchPattern":"try {\n  const cycles = await eolProduct('nodejs');\n} catch (e) {\n  if (e instanceof EmptyResultError) console.warn(`no cycles for product: ${e.message}`);\n  else throw e;\n}","preventionTips":["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"],"tags":["api","empty-result","endoflife","data"],"backgroundTag":"empty-api-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}