jackwener/OpenCLI · error · CommandExecutionError

github-trending parser drift: missing repository link

Error message

github-trending parser drift: missing repository link

What it means

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.

Source

Thrown at clis/github-trending/repos.js:65

        || /no trending repositories/i.test(stripTags(html));
}

function parseTrendingHtml(html, limit) {
    const blocks = Array.from(String(html ?? '').matchAll(/<article\b[^>]*class="[^"]*\bBox-row\b[^"]*"[^>]*>([\s\S]*?)<\/article>/g))
        .map((match) => match[1]);
    const rows = [];

    if (blocks.length === 0) {
        if (hasExplicitEmptyTrending(html)) return rows;
        throw new CommandExecutionError('github-trending parser drift: no repository rows found');
    }

    for (const raw of blocks) {
        const block = raw;

        const nameMatch = block.match(/<h2\b[\s\S]*?href="\/([^"/?#]+\/[^"/?#]+)"/);
        if (!nameMatch) {
            throw new CommandExecutionError('github-trending parser drift: missing repository link');
        }
        const repo = decodeHtmlEntities(nameMatch[1]).trim();
        if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repo)) {
            throw new CommandExecutionError(`github-trending parser drift: invalid repository identity "${repo}"`);
        }

        const descMatch = block.match(/<p class="col-9 color-fg-muted[^"]*">([\s\S]*?)<\/p>/);
        const description = descMatch
            ? decodeHtmlEntities(stripTags(descMatch[1]).replace(/\s+/g, ' ')).trim()
            : '';

        const langMatch = block.match(/<span itemprop="programmingLanguage">([\s\S]*?)<\/span>/);
        const language = langMatch ? decodeHtmlEntities(stripTags(langMatch[1])).trim() : null;

        const escapedRepo = escapeRegExp(repo);
        const starsMatch = block.match(new RegExp(`<a\\b[^>]*href="/${escapedRepo}/stargazers"[^>]*>([\\s\\S]*?)</a>`));
        const forksMatch = block.match(new RegExp(`<a\\b[^>]*href="/${escapedRepo}/forks"[^>]*>([\\s\\S]*?)</a>`));
        const sinceMatch = block.match(/([\d,]+)\s+stars\s+(?:today|this week|this month)/i);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Upgrade the CLI/scraper package to a version matching the current GitHub markup
  2. Retry later — mid-deploy page versions can transiently mismatch
  3. Inspect the failing HTML and adjust the h2/href regex locally, then report upstream

Example fix

// before
const nameMatch = block.match(/<h2\b[\s\S]*?href="\/([^"]+)"/);
// after: fall back to any repo link in the row
const nameMatch = block.match(/<h2\b[\s\S]*?href="\/([^"]+)"/)
  ?? block.match(/href="\/([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+)"/);
Defensive patterns

Strategy: fallback

Validate before calling

null

Type guard

function isParserDrift(e) { return e instanceof Error && /parser drift/.test(e.message); }

Try / catch

try {
  const rows = parseTrendingHtml(html);
} catch (e) {
  if (/missing repository link/.test(e.message)) { logHtmlSample(html); return partialResults(); }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/0520536bd1ff11a7. Report an issue: GitHub.