{"record":{"id":"455e9f8d39f1baac","repo":"jackwener/OpenCLI","slug":"match-map-parser-requires-a-stats-matches-mapstat","errorCode":null,"errorMessage":"match map parser requires a /stats/matches/mapstatsid/:id/:slug URL","messagePattern":"match map parser requires a /stats/matches/mapstatsid/:id/:slug URL","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/hltv/utils.js","lineNumber":521,"sourceCode":"\n  await gotoAndWait(page, url, 'a[href*=\"/stats/matches/\"]', 'hltv match series link page');\n  const seriesUrl = await page.evaluate((payload) => {\n    for (const a of document.querySelectorAll('a[href*=\"/stats/matches/\"]')) {\n      const href = new URL(a.getAttribute('href'), payload.base);\n      href.search = '';\n      href.hash = '';\n      if (/^\\/stats\\/matches\\/\\d+\\//.test(href.pathname)) return href.toString();\n    }\n    return null;\n  }, { base: BASE });\n\n  return seriesUrl ?? url.toString();\n}\n\nexport async function readMatchMap(page, match) {\n  const url = normalizeMatchUrl(match);\n  if (!/^\\/stats\\/matches\\/mapstatsid\\/\\d+\\//.test(url.pathname)) {\n    throw new ArgumentError('match map parser requires a /stats/matches/mapstatsid/:id/:slug URL');\n  }\n  const matchStatsId = extractIdFromUrl(url.toString(), 'matchStats');\n\n  await gotoAndWait(page, url, '.stats-section.stats-match, .stats-table.totalstats', 'hltv match map page');\n\n  const rows = await page.evaluate((payload) => {\n    const clean = (value) => String(value ?? '').replace(/\\s+/g, ' ').trim();\n    const cleanLines = (value) => String(value ?? '').split('\\n').map((line) => clean(line)).filter(Boolean);\n    const numberFrom = (value) => {\n      const match = String(value ?? '').replace(/,/g, '').match(/-?\\d+(?:\\.\\d+)?/);\n      return match ? Number(match[0]) : null;\n    };\n    const textOf = (root, selector) => clean(root.querySelector(selector)?.textContent);\n    const splitMainParen = (value) => {\n      const match = clean(value).match(/^(-?\\d+(?:\\.\\d+)?)\\s*(?:\\(([-\\d.]+)\\))?/);\n      return { main: match ? Number(match[1]) : null, paren: match?.[2] !== undefined ? Number(match[2]) : null };\n    };\n    const infoLines = cleanLines(document.querySelector('.match-info-box')?.innerText);","sourceCodeStart":503,"sourceCodeEnd":539,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/hltv/utils.js#L503-L539","documentation":"readMatchMap() parses a single map's stats page and requires the input to be a map-stats URL (/stats/matches/mapstatsid/:id/:slug). It normalizes the match argument and throws ArgumentError if the pathname does not match that pattern. This guard exists because the parser also extracts the numeric matchStatsId from the URL and navigates directly to the map page.","triggerScenarios":"Calling readMatchMap(page, match) with a regular match URL (/matches/:id/:slug), a series stats URL (/stats/matches/:id/:slug), an ID-only string, or a URL with a trailing slash missing after the numeric ID.","commonSituations":"Confusing the public match URL with the per-map stats URL (these are different HLTV pages); reusing a URL collected from a series page instead of the mapstatsid links; hand-building the URL and forgetting the slug/trailing slash.","solutions":["Pass a mapstatsid URL, e.g. from resolveMatchMapUrls()/resolveStatsSeriesUrlFromMap() output","Verify the path matches /stats/matches/mapstatsid/<digits>/... including the trailing slash and slug","If you only have a match ID, first navigate the match page flow to obtain the mapstatsid URLs, then call readMatchMap for each"],"exampleFix":"// before\nawait readMatchMap(page, 'https://www.hltv.org/stats/matches/92734/natus-vincere-vs-faze');\n// after\nawait readMatchMap(page, 'https://www.hltv.org/stats/matches/mapstatsid/73681/natus-vincere-vs-faze-map1');","handlingStrategy":"validation","validationCode":"function requireMapStatsUrl(match) {\n  const u = new URL(String(match), 'https://www.hltv.org');\n  if (!/^\\/stats\\/matches\\/mapstatsid\\/\\d+\\//.test(u.pathname)) {\n    throw new Error(`readMatchMap needs a mapstatsid URL, got: ${u.pathname}`);\n  }\n  return u.toString();\n}\nawait readMatchMap(page, requireMapStatsUrl(match));","typeGuard":"function isMapStatsInput(v) {\n  try {\n    return /^\\/stats\\/matches\\/mapstatsid\\/\\d+\\//.test(new URL(String(v), 'https://www.hltv.org').pathname);\n  } catch { return false; }\n}","tryCatchPattern":"try {\n  const map = await readMatchMap(page, match);\n} catch (err) {\n  if (err instanceof ArgumentError && /match map parser requires/.test(err.message)) {\n    console.error('Pass a /stats/matches/mapstatsid/:id/:slug URL, not:', match);\n  } else throw err;\n}","preventionTips":["Distinguish match URLs (/matches/:id) from map stats URLs (/stats/matches/mapstatsid/:id) in your pipeline","Normalize inputs through one helper that asserts the mapstatsid shape","When iterating maps of a match, use the mapstatsid links collected from the match/series page"],"tags":["hltv","argument-validation","url-format"],"backgroundTag":"invalid-url-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}