{"record":{"id":"e6f05e85c647b957","repo":"jackwener/OpenCLI","slug":"offset-must-be-a-multiple-of-100","errorCode":null,"errorMessage":"offset must be a multiple of 100","messagePattern":"offset must be a multiple of 100","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/hltv/utils.js","lineNumber":75,"sourceCode":"export const VERSIONS = {\n  both: null,\n  cs2: 'CS2',\n  csgo: 'CSGO',\n};\n\nexport function normalizeLimit(value, defaultValue, maxValue, label = 'limit') {\n  const raw = value ?? defaultValue;\n  const n = Number(raw);\n  if (!Number.isInteger(n) || n <= 0) throw new ArgumentError(`${label} must be a positive integer`);\n  if (n > maxValue) throw new ArgumentError(`${label} must be <= ${maxValue}`);\n  return n;\n}\n\nexport function normalizeOffset(value, defaultValue = 0) {\n  const raw = value ?? defaultValue;\n  const n = Number(raw);\n  if (!Number.isInteger(n) || n < 0) throw new ArgumentError('offset must be a non-negative integer');\n  if (n % 100 !== 0) throw new ArgumentError('offset must be a multiple of 100');\n  return n;\n}\n\nexport function normalizeChoice(value, defaultValue, choices, label) {\n  const raw = String(value ?? defaultValue);\n  if (!Object.prototype.hasOwnProperty.call(choices, raw)) {\n    throw new ArgumentError(`${label} must be one of: ${Object.keys(choices).join(', ')}`);\n  }\n  return raw;\n}\n\nexport function parseNumber(value) {\n  const raw = String(value ?? '').replace(/\\s+/g, ' ').trim();\n  if (!raw || raw === '-' || raw.toLowerCase() === 'n/a') return null;\n  const match = raw.replace(/,/g, '').match(/-?\\d+(?:\\.\\d+)?/);\n  if (!match) return null;\n  const n = Number(match[0]);\n  return Number.isFinite(n) ? n : null;","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/hltv/utils.js#L57-L93","documentation":"normalizeOffset additionally requires the offset to be a multiple of 100 because the upstream HLTV stats API pages in blocks of exactly 100 results. Any valid non-negative integer that is not divisible by 100 triggers this error.","triggerScenarios":"--offset 150, --offset 250, or programmatic offsets computed from a limit other than 100 (e.g. offset += 50 per page).","commonSituations":"Users combining offset with a custom limit like --limit 50 and stepping offset by 50; porting code from APIs with arbitrary offsets.","solutions":["Step the offset by 100 between pages (offset = 0, 100, 200, ...)","If using a custom limit, still align offsets to multiples of 100","Track page count and compute offset as pageCount * 100"],"exampleFix":"// before\nfor (let offset = 0; ; offset += 50) { ... }\n// after\nfor (let offset = 0; ; offset += 100) { ... }","handlingStrategy":"validation","validationCode":"const n = Number(opts.offset);\nif (!Number.isInteger(n) || n < 0 || n % 100 !== 0) throw new Error('offset must be a non-negative multiple of 100');","typeGuard":"const isAlignedOffset = (v) => Number.isInteger(Number(v)) && Number(v) >= 0 && Number(v) % 100 === 0;","tryCatchPattern":"try {\n  return await hltv.stats({ offset });\n} catch (e) {\n  if (e.name === 'ArgumentError' && e.message.includes('multiple of 100')) {\n    return hltv.stats({ offset: Math.floor(offset / 100) * 100 });\n  }\n  throw e;\n}","preventionTips":["Always step paging loops by 100 regardless of the limit value","Snap offsets with Math.floor(offset / 100) * 100 before calls","Keep a single paging helper so the step size lives in one place"],"tags":["cli","validation","paging","argument-error"],"backgroundTag":"invalid-argument-value","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}