jackwener/OpenCLI · warning · EmptyResultError
No last encounter found for ${playerA.playerId} and ${player
Error message
No last encounter found for ${playerA.playerId} and ${playerB.playerId} in the latest ${recentMaps} maps from playerA What it means
findLastEncounterSeries scans playerA's most recent maps for a mapstats page where both playerA and playerB appear, and throws this EmptyResultError when no such shared map is found within the requested window. The library treats 'these two players have not met in the analyzed range' as an empty result rather than a bug. It is raised from the public findLastEncounterSeries, called by lastEncounter.
Source
Thrown at clis/hltv/player-duel.js:267
for (const candidate of candidates) {
const mapUrl = candidate.details?.matchStatsUrl;
if (!mapUrl || seen.has(candidate.matchStatsId)) continue;
seen.add(candidate.matchStatsId);
if (!playerBMatchIds.has(candidate.matchStatsId)) continue;
const mapRows = await readMatchMap(page, mapUrl);
if (!findPlayer(mapRows, playerB.playerId)) continue;
const seriesUrl = await resolveStatsSeriesUrlFromMap(page, mapUrl);
const mapUrls = await resolveMatchMapUrls(page, seriesUrl);
return {
mapUrls,
matchedMapStatsId: candidate.matchStatsId,
seriesUrl,
};
}
throw new EmptyResultError(
'hltv player-duel',
`No last encounter found for ${playerA.playerId} and ${playerB.playerId} in the latest ${recentMaps} maps from playerA`,
);
}
async function buildDuelRows(page, args, mapUrls, scope, limit, context = {}) {
const playerA = parsePlayerRef(args.playerA);
const playerB = parsePlayerRef(args.playerB);
const rows = [];
for (const mapUrl of mapUrls.slice(0, limit)) {
const mapRows = await readMatchMap(page, mapUrl);
const playerARow = findPlayer(mapRows, playerA.playerId);
const playerBRow = findPlayer(mapRows, playerB.playerId);
if (!playerARow || !playerBRow) continue;
const matrix = playerARow.team === playerBRow.team ? [] : await readPerformanceKillMatrix(page, mapUrl);
const direct = parseDirectDuel(matrix, playerARow.playerName, playerBRow.playerName);
rows.push(toMapRow(scope, mapUrl, playerARow, playerBRow, direct));View on GitHub (pinned to 49907e53dc)
Solutions
- Increase the recentMaps argument to cover a longer window so the last encounter falls inside it
- Verify both playerIds/slugs resolve to active HLTV profiles with recent matches
- Check manually that the two players actually faced each other recently before calling
- Handle EmptyResultError at the call site and report 'no encounter' as a valid outcome
Example fix
// before
const series = await findLastEncounterSeries(page, { playerA, playerB, recentMaps: 20 });
// after
let series;
try {
series = await findLastEncounterSeries(page, { playerA, playerB, recentMaps: 20 });
} catch (err) {
if (err instanceof EmptyResultError) series = null;
else throw err;
} Defensive patterns
Strategy: try-catch
Validate before calling
// ensure window is wide enough before calling if (!Number.isInteger(recentMaps) || recentMaps < 50) recentMaps = 100;
Type guard
function hasRecentMaps(playerA) { return Number.isFinite(playerA?.playerId); } Try / catch
try {
const series = await findLastEncounterSeries(page, { playerA, playerB, recentMaps });
} catch (err) {
if (err instanceof EmptyResultError) return null; // no encounter is a valid answer
throw err;
} Prevention
- Use a generous recentMaps window (e.g. 100) for rivals who meet rarely
- Validate both playerId values resolve to active profiles first
- Treat EmptyResultError as a legitimate 'no data' answer, not a crash
- Log playerIds with the error to diagnose which ref is wrong
When it happens
Trigger: Calling findLastEncounterSeries/lastEncounter with two player refs whose recent match histories share no mapstats pages, or with recentMaps set too small to cover their last head-to-head; one player has too few parsed rows or rows with missing matchStatsId.
Common situations: Comparing players from different regions/tiers who never met; shrinking recentMaps below the distance to their last real encounter; a player's recent maps mostly forfeits/removed matches not present in mapstats; stale player IDs after HLTV profile changes.
Understand the failure class
Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.
Related errors
- No shared map rows found for ${playerA.playerId} and ${playe
- No map-pool rows found for team ${teamId}/${slug}
- No trains found from ${fromStation.name} to ${toStation.name
- NO_DATA
- No Wayback snapshots for "${target}".
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/e348295705ae973f.
Report an issue: GitHub.