{"record":{"id":"c554bf4b9dfb88a0","repo":"jackwener/OpenCLI","slug":"timeouterror","errorCode":null,"errorMessage":"TimeoutError","messagePattern":"TimeoutError","errorType":"exception","errorClass":"TimeoutError","httpStatus":null,"severity":"error","filePath":"clis/hltv/utils.js","lineNumber":845,"sourceCode":"          rowKillsCol: parsed.left,\n          colKillsRow: parsed.right,\n        });\n      }\n    }\n    return entries;\n  });\n\n  if (!Array.isArray(matrix)) return [];\n  return matrix;\n}\n\nexport async function gotoAndWait(page, url, selector, label) {\n  try {\n    await page.goto(url.toString(), { waitUntil: 'domcontentloaded', settleMs: 1000, timeout: 20000 });\n    await page.wait({ selector, timeout: 15000 });\n  } catch (error) {\n    if (/timeout/i.test(String(error?.message ?? error))) {\n      throw new TimeoutError(label, 15);\n    }\n    throw new CommandExecutionError(`${label} failed: ${error?.message ?? error}`);\n  }\n}\n\nexport function assertRows(rows, command) {\n  if (!Array.isArray(rows)) throw new CommandExecutionError(`${command} parser returned an unexpected shape`);\n  if (rows.length === 0) throw new EmptyResultError(command, 'No rows were found in the visible HLTV page');\n  return rows;\n}\n\nexport function assertRequiredFields(rows, command, fields) {\n  assertRows(rows, command);\n  for (const [index, row] of rows.entries()) {\n    for (const field of fields) {\n      if (row?.[field] === null || row?.[field] === undefined || row?.[field] === '') {\n        throw new CommandExecutionError(`${command} parser returned row ${index + 1} without required ${field}`);\n      }","sourceCodeStart":827,"sourceCodeEnd":863,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/hltv/utils.js#L827-L863","documentation":"gotoAndWait() wraps page.goto (20s timeout) and page.wait (15s timeout) in a try/catch; if the underlying error message matches /timeout/i it rethrows a TimeoutError labeled with the target page and a 15-second value. It means the HLTV page did not load within the budget or the expected selector never appeared within 15 seconds.","triggerScenarios":"Calling any helper that uses gotoAndWait (e.g. readMatchMap, resolveStatsSeriesUrlFromMap) when HLTV is slow or unreachable, the anti-bot/DDoS-guard page never yields the expected selector, the URL 404s slowly, or the network/proxy is too slow for the 15s selector wait.","commonSituations":"Scraping under heavy rate limiting (HLTV serves interstitials that never contain '.stats-section.stats-match' or 'a[href*=\"/stats/matches/\"]'); flaky proxies;HLTV outages or maintenance; overly aggressive parallel navigation on a single page handle.","solutions":["Retry the navigation with exponential backoff — transient slowness and guard pages usually clear","Check general connectivity to hltv.org (curl the URL) and your proxy health","Serialize navigations / reduce concurrency if a single browser page is reused for overlapping waits","Increase the timeout budgets in gotoAndWait if legitimate pages are just slow","If timeouts cluster on specific URLs, verify those pages still exist and render the expected selector"],"exampleFix":"// before\nconst rows = await readMatchMap(page, mapstatsUrl); // TimeoutError: hltv match map page\n// after\nasync function withRetry(fn, attempts = 3) {\n  for (let i = 0; ; i++) {\n    try { return await fn(); }\n    catch (err) {\n      if (i + 1 >= attempts || !/timeout/i.test(String(err?.message ?? err))) throw err;\n      await new Promise((r) => setTimeout(r, 2000 * 2 ** i));\n    }\n  }\n}\nconst rows = await withRetry(() => readMatchMap(page, mapstatsUrl));","handlingStrategy":"retry","validationCode":"// preflight: confirm the page is reachable before spending the 20s+15s budgets\nconst res = await fetch(url, { method: 'HEAD' });\nif (!res.ok) throw new Error(`page unreachable: ${res.status}`);","typeGuard":null,"tryCatchPattern":"try {\n  const rows = await readMatchMap(page, mapstatsUrl);\n} catch (err) {\n  if (err instanceof TimeoutError) {\n    await new Promise((r) => setTimeout(r, 2000));\n    return readMatchMap(page, mapstatsUrl); // retry with backoff\n  }\n  throw err;\n}","preventionTips":["Retry navigations with exponential backoff on TimeoutError","Reduce concurrency per browser page; serialize gotoAndWait calls","Monitor proxy health and HLTV availability before batch runs","Increase the 15s/20s budgets in gotoAndWait if your network is slow"],"tags":["hltv","timeout","network","scraping"],"backgroundTag":"page-load-timeout","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}