{"record":{"id":"1edb0196fc4c4939","repo":"jackwener/OpenCLI","slug":"hltv-match-map-parser-returned-an-unexpected-shape","errorCode":null,"errorMessage":"hltv match-map parser returned an unexpected shape","messagePattern":"hltv match-map parser returned an unexpected shape","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/hltv/utils.js","lineNumber":601,"sourceCode":"          dateTime,\n          teamScore: currentTeam === teamOne ? teamOneScore : teamTwoScore,\n          opponent,\n          opponentScore: currentTeam === teamOne ? teamTwoScore : teamOneScore,\n          headshots: kills.paren,\n          assists: assists.main,\n          flashAssists: assists.paren,\n          tradedDeaths: deaths.paren,\n          multiKills: numberFrom(textOf(tr, '.st-mks')),\n          clutches: numberFrom(textOf(tr, '.st-clutches')),\n          roundSwingPct: numberFrom(textOf(tr, '.st-roundSwing')),\n        },\n        url: playerUrl,\n      });\n    }\n    return rows;\n  }, { base: BASE, matchStatsId });\n\n  if (!Array.isArray(rows)) throw new CommandExecutionError('hltv match-map parser returned an unexpected shape');\n  if (rows.length === 0) throw new CommandExecutionError('hltv match-map parser found no player rows');\n\n  return assertRequiredFields(rows.map((row) => ({\n    matchStatsId: row.matchStatsId,\n    playerId: row.playerId,\n    playerName: row.playerName,\n    team: row.team,\n    kills: parseNumber(row.kills),\n    deaths: parseNumber(row.deaths),\n    adr: parseNumber(row.adr),\n    kastPct: parseNumber(row.kastPct),\n    rating: parseNumber(row.rating),\n    opKd: row.opKd,\n    details: row.details,\n    url: row.url,\n  })), 'hltv match-map', ['matchStatsId', 'playerId', 'playerName', 'team', 'url']);\n}\n","sourceCodeStart":583,"sourceCodeEnd":619,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/hltv/utils.js#L583-L619","documentation":"After gotoAndWait loads the map-stats page, readMatchMap runs page.evaluate() to scrape player rows into an array. If the browser-side serializer returns something other than an array (e.g. null/undefined because evaluation failed or the page was replaced), a CommandExecutionError with this message is thrown. It is a defensive postcondition on the parser's return shape.","triggerScenarios":"page.evaluate returning non-array — typically when the page navigation was hijacked (login wall, Cloudflare/DDoS-guard interstitial, error page) or the execution context was destroyed mid-evaluation and the wrapper returned undefined.","commonSituations":"Scraping behind HLTV's anti-bot interstitial pages; running against a stale or crashed page target; proxy or network middlewares injecting HTML so the evaluate context differs; heavy rate-limiting causing the loaded document to not be a stats page.","solutions":["Re-run with backoff; if the page was an anti-bot interstitial, retrying after the block clears usually yields a proper array","Verify the URL actually renders the stats section by checking for '.stats-table.totalstats' in the loaded page","Slow down request rate / rotate proxies if HLTV is serving guard pages","Update the library if HLTV changed its page structure so the evaluate no longer completes normally"],"exampleFix":"// before\nconst rows = await readMatchMap(page, url); // throws on interstitial\n// after\ntry {\n  const rows = await readMatchMap(page, url);\n} catch (err) {\n  if (/unexpected shape/.test(err.message)) {\n    await sleep(5000);\n    return readMatchMap(page, url); // retry once\n  }\n  throw err;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  const rows = await readMatchMap(page, mapstatsUrl);\n} catch (err) {\n  if (/unexpected shape/.test(String(err?.message))) {\n    await sleep(5000);\n    return readMatchMap(page, mapstatsUrl); // retry — page may have been a guard page\n  }\n  throw err;\n}","preventionTips":["Retry transient parse failures with backoff","Watch for anti-bot/interstitial responses and slow down or rotate proxies","Verify the loaded page contains '.stats-table.totalstats' before treating failures as data problems"],"tags":["hltv","scraping","parser","command-execution"],"backgroundTag":"unexpected-response-shape","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}