jackwener/OpenCLI · error · CommandExecutionError

hltv match-map parser found no player rows

Error message

hltv match-map parser found no player rows

What it means

The map-stats page loaded and page.evaluate returned an array, but zero player rows were extracted from '.stats-table.totalstats tr'. readMatchMap treats an empty table as an error (CommandExecutionError) because a valid map stats page always lists the players of both teams.

Source

Thrown at clis/hltv/utils.js:602

          teamScore: currentTeam === teamOne ? teamOneScore : teamTwoScore,
          opponent,
          opponentScore: currentTeam === teamOne ? teamTwoScore : teamOneScore,
          headshots: kills.paren,
          assists: assists.main,
          flashAssists: assists.paren,
          tradedDeaths: deaths.paren,
          multiKills: numberFrom(textOf(tr, '.st-mks')),
          clutches: numberFrom(textOf(tr, '.st-clutches')),
          roundSwingPct: numberFrom(textOf(tr, '.st-roundSwing')),
        },
        url: playerUrl,
      });
    }
    return rows;
  }, { base: BASE, matchStatsId });

  if (!Array.isArray(rows)) throw new CommandExecutionError('hltv match-map parser returned an unexpected shape');
  if (rows.length === 0) throw new CommandExecutionError('hltv match-map parser found no player rows');

  return assertRequiredFields(rows.map((row) => ({
    matchStatsId: row.matchStatsId,
    playerId: row.playerId,
    playerName: row.playerName,
    team: row.team,
    kills: parseNumber(row.kills),
    deaths: parseNumber(row.deaths),
    adr: parseNumber(row.adr),
    kastPct: parseNumber(row.kastPct),
    rating: parseNumber(row.rating),
    opKd: row.opKd,
    details: row.details,
    url: row.url,
  })), 'hltv match-map', ['matchStatsId', 'playerId', 'playerName', 'team', 'url']);
}

function round(value, digits = 2) {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Retry the request after a short delay — the table may render later than the waited selector
  2. Check HLTV in a browser for the specific mapstatsid to confirm the table exists; skip maps without recorded stats
  3. Inspect the loaded HTML; if selectors changed, update or upgrade the library's parser
  4. Reduce scraping rate / change IP if guard pages are suspected

Example fix

// before
const rows = await readMatchMap(page, mapstatsUrl); // throws when table is empty
// after
let rows;
try {
  rows = await readMatchMap(page, mapstatsUrl);
} catch (err) {
  if (/found no player rows/.test(err.message)) {
    await sleep(3000);
    rows = await readMatchMap(page, mapstatsUrl);
  } else throw err;
}
Defensive patterns

Strategy: retry

Try / catch

try {
  const rows = await readMatchMap(page, mapstatsUrl);
} catch (err) {
  if (/found no player rows/.test(String(err?.message))) {
    await new Promise((r) => setTimeout(r, 3000));
    return readMatchMap(page, mapstatsUrl); // second failure => treat as data-less map
  }
  throw err;
}

Prevention

When it happens

Trigger: Navigating to a mapstatsid page whose stats table has not rendered (page still loading beyond the wait selector), a completed-but-empty/forfeited map, HLTV layout changes renaming .stats-table.totalstats or .st-teamname/player link selectors, or being served an anti-bot/redirect page that still contains the waited selector.

Common situations: HLTV markup changes after a site redesign; scraping very old maps with a different table layout; rate-limited sessions receiving simplified pages; map pages for matches that were forfeited or where stats were never recorded.

Related errors


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