{"record":{"id":"7b371efa835ec8fd","repo":"jackwener/OpenCLI","slug":"limit-must-be-max","errorCode":null,"errorMessage":"limit must be <= ${max}","messagePattern":"limit must be <= (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/12306/utils.js","lineNumber":111,"sourceCode":"    if (!DATE_RE.test(String(value ?? ''))) {\n        throw new ArgumentError(`date must be YYYY-MM-DD, got \"${value}\"`);\n    }\n    const [y, m, d] = value.split('-').map(Number);\n    const date = new Date(Date.UTC(y, m - 1, d));\n    if (date.getUTCFullYear() !== y || date.getUTCMonth() !== m - 1 || date.getUTCDate() !== d) {\n        throw new ArgumentError(`date \"${value}\" is not a real calendar date`);\n    }\n    return value;\n}\n\nexport function normalizeLimit(value, defaultValue, max) {\n    if (value === undefined || value === null || value === '') return defaultValue;\n    const n = Number(value);\n    if (!Number.isInteger(n) || n < 1) {\n        throw new ArgumentError(`limit must be a positive integer (1-${max})`);\n    }\n    if (n > max) {\n        throw new ArgumentError(`limit must be <= ${max}`);\n    }\n    return n;\n}\n\n/** Extract Set-Cookie header values into a single `Cookie:` header string. */\nexport function buildCookieHeader(setCookieHeaders) {\n    if (!Array.isArray(setCookieHeaders) || setCookieHeaders.length === 0) return '';\n    return setCookieHeaders\n        .map((line) => line.split(';')[0])\n        .filter(Boolean)\n        .join('; ');\n}\n\nexport async function fetchStationBundle(fetchImpl = fetch) {\n    const resp = await fetchImpl(STATION_BUNDLE_URL, {\n        headers: { 'User-Agent': UA },\n    });\n    if (!resp.ok) {","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/12306/utils.js#L93-L129","documentation":"normalizeLimit() also enforces an upper bound: values greater than max throw this ArgumentError. The cap protects the 12306 API and result rendering from unbounded queries.","triggerScenarios":"Passing limit above the command's maximum, e.g. limit=1000 when the command caps results, or limit=Infinity from a computed default.","commonSituations":"Developers assuming there is no cap and requesting 'everything', or copying a limit valid for one command into another with a lower max.","solutions":["Lower the limit to the command's documented maximum (read from the error message)","Omit limit to use the default within the allowed range","If you need more results, paginate or refine the query rather than raising the limit"],"exampleFix":"// before\nawait query({ limit: 500 }); // max is 100\n// after\nawait query({ limit: 100 }); // within max","handlingStrategy":"validation","validationCode":"function isWithinLimit(v, max) {\n  const n = Number(v);\n  return Number.isInteger(n) && n >= 1 && n <= max;\n}\nif (!isWithinLimit(500, MAX_LIMIT)) throw new Error(`limit must be <= ${MAX_LIMIT}`);","typeGuard":null,"tryCatchPattern":"try {\n  await query({ limit });\n} catch (e) {\n  if (e instanceof ArgumentError && e.message.startsWith('limit must be <=')) {\n    console.error(`Cap the limit at the documented maximum or paginate instead.`);\n  } else throw e;\n}","preventionTips":["Clamp limits with Math.min(requested, max) before calling","Read each command's max from docs/help — caps differ per command","Paginate instead of requesting everything at once"],"tags":["input-validation","argument-error","pagination"],"backgroundTag":"invalid-parameter-value","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}