{"record":{"id":"0520536bd1ff11a7","repo":"jackwener/OpenCLI","slug":"github-trending-parser-drift-missing-repository-l","errorCode":null,"errorMessage":"github-trending parser drift: missing repository link","messagePattern":"github-trending parser drift: missing repository link","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/github-trending/repos.js","lineNumber":65,"sourceCode":"        || /no trending repositories/i.test(stripTags(html));\n}\n\nfunction parseTrendingHtml(html, limit) {\n    const blocks = Array.from(String(html ?? '').matchAll(/<article\\b[^>]*class=\"[^\"]*\\bBox-row\\b[^\"]*\"[^>]*>([\\s\\S]*?)<\\/article>/g))\n        .map((match) => match[1]);\n    const rows = [];\n\n    if (blocks.length === 0) {\n        if (hasExplicitEmptyTrending(html)) return rows;\n        throw new CommandExecutionError('github-trending parser drift: no repository rows found');\n    }\n\n    for (const raw of blocks) {\n        const block = raw;\n\n        const nameMatch = block.match(/<h2\\b[\\s\\S]*?href=\"\\/([^\"/?#]+\\/[^\"/?#]+)\"/);\n        if (!nameMatch) {\n            throw new CommandExecutionError('github-trending parser drift: missing repository link');\n        }\n        const repo = decodeHtmlEntities(nameMatch[1]).trim();\n        if (!/^[A-Za-z0-9_.-]+\\/[A-Za-z0-9_.-]+$/.test(repo)) {\n            throw new CommandExecutionError(`github-trending parser drift: invalid repository identity \"${repo}\"`);\n        }\n\n        const descMatch = block.match(/<p class=\"col-9 color-fg-muted[^\"]*\">([\\s\\S]*?)<\\/p>/);\n        const description = descMatch\n            ? decodeHtmlEntities(stripTags(descMatch[1]).replace(/\\s+/g, ' ')).trim()\n            : '';\n\n        const langMatch = block.match(/<span itemprop=\"programmingLanguage\">([\\s\\S]*?)<\\/span>/);\n        const language = langMatch ? decodeHtmlEntities(stripTags(langMatch[1])).trim() : null;\n\n        const escapedRepo = escapeRegExp(repo);\n        const starsMatch = block.match(new RegExp(`<a\\\\b[^>]*href=\"/${escapedRepo}/stargazers\"[^>]*>([\\\\s\\\\S]*?)</a>`));\n        const forksMatch = block.match(new RegExp(`<a\\\\b[^>]*href=\"/${escapedRepo}/forks\"[^>]*>([\\\\s\\\\S]*?)</a>`));\n        const sinceMatch = block.match(/([\\d,]+)\\s+stars\\s+(?:today|this week|this month)/i);","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/github-trending/repos.js#L47-L83","documentation":"A repository row (Box-row article) matched the block regex, but no `<h2>` element containing an href like '/owner/repo' was found inside it. The parser throws this CommandExecutionError because a repo row without an identifiable link means GitHub's trending markup changed and results would be unreliable.","triggerScenarios":"parseTrendingHtml iterates a matched article block whose regex `/<h2\\b[\\s\\S]*?href=\"\\/([^\"/?#]+\\/[^\"/?#]+)\"/` finds no match — e.g. GitHub renames the h2 heading element, changes the link structure, or moves the repo link outside an h2.","commonSituations":"GitHub UI redesign altering heading tags or repository link placement; truncated/partial HTML responses; server-side experiment variants where the link is rendered via a different element.","solutions":["Upgrade the CLI/scraper package to a version matching the current GitHub markup","Retry later — mid-deploy page versions can transiently mismatch","Inspect the failing HTML and adjust the h2/href regex locally, then report upstream"],"exampleFix":"// before\nconst nameMatch = block.match(/<h2\\b[\\s\\S]*?href=\"\\/([^\"]+)\"/);\n// after: fall back to any repo link in the row\nconst nameMatch = block.match(/<h2\\b[\\s\\S]*?href=\"\\/([^\"]+)\"/)\n  ?? block.match(/href=\"\\/([A-Za-z0-9_.-]+\\/[A-Za-z0-9_.-]+)\"/);","handlingStrategy":"fallback","validationCode":"null","typeGuard":"function isParserDrift(e) { return e instanceof Error && /parser drift/.test(e.message); }","tryCatchPattern":"try {\n  const rows = parseTrendingHtml(html);\n} catch (e) {\n  if (/missing repository link/.test(e.message)) { logHtmlSample(html); return partialResults(); }\n  throw e;\n}","preventionTips":["Save a sample of failing HTML to diagnose selector drift quickly","Test the parser against fixture HTML on every GitHub UI release","Upgrade promptly when this error appears — it indicates markup changes"],"tags":["scraping","parser-drift","github","html-parsing"],"backgroundTag":"parser-drift-html-structure-changed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}