{"record":{"id":"1c0a74a33b7035c1","repo":"jackwener/OpenCLI","slug":"command-parser-returned-an-unexpected-shape","errorCode":null,"errorMessage":"${command} parser returned an unexpected shape","messagePattern":"(.+?) parser returned an unexpected shape","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/hltv/utils.js","lineNumber":852,"sourceCode":"\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      }\n    }\n  }\n  return rows;\n}\n","sourceCodeStart":834,"sourceCodeEnd":868,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/hltv/utils.js#L834-L868","documentation":"assertRows validates that a command's parser returned an array before further processing; if the parsed value is not an array it throws CommandExecutionError('<command> parser returned an unexpected shape'). It guards against HLTV DOM changes that make the parser return an object, null, or undefined.","triggerScenarios":"A parse function returns anything other than an array — e.g. page.evaluate returns undefined after an HLTV markup change, or a parser bug returns a single object instead of an array.","commonSituations":"HLTV redesigns a page so selectors match nothing and the parser silently returns null/undefined; a newly added command's parser has a bug.","solutions":["Log what the parser actually returned to confirm the shape","Update the parser's selectors for the current HLTV markup","Upgrade the CLI if a newer version has fixed parsers for the redesign","Wrap the call in try/catch for CommandExecutionError and degrade gracefully"],"exampleFix":"// before\nconst rows = parseMatches(doc);\nassertRows(rows, 'matches');\n// after\nconst parsed = parseMatches(doc);\nif (!Array.isArray(parsed)) {\n  console.warn('HLTV markup changed; matches parser returned', typeof parsed);\n}\nconst rows = assertRows(parsed, 'matches');","handlingStrategy":"type-guard","validationCode":"const parsed = parseRows(doc);\nif (!Array.isArray(parsed)) {\n  throw new Error(`parser returned ${typeof parsed}, expected array`);\n}","typeGuard":"function isRowArray(value) {\n  return Array.isArray(value) && value.every(v => v !== null && typeof v === 'object');\n}","tryCatchPattern":"try {\n  const rows = assertRows(parsed, command);\n} catch (err) {\n  if (err.name === 'CommandExecutionError' && /unexpected shape/.test(err.message)) {\n    console.error(`${command}: HLTV markup may have changed; got non-array from parser`);\n    return [];\n  }\n  throw err;\n}","preventionTips":["Always Array.isArray-check parser output before consuming it","Pin/upgrade the CLI version when HLTV redesigns its pages","Add a smoke test per command that asserts a non-empty row array","Log raw parser output on failure for faster diagnosis"],"tags":["parsing","schema-mismatch","scraping"],"backgroundTag":"schema-validation-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}