{"record":{"id":"6b748b1cd4e1a4b5","repo":"jackwener/OpenCLI","slug":"openalex-label-must-be-maxvalue","errorCode":null,"errorMessage":"openalex ${label} must be <= ${maxValue}","messagePattern":"openalex (.+?) must be <= (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/openalex/utils.js","lineNumber":33,"sourceCode":"const WORK_ID = /^W\\d{4,}$/;\n// DOIs are loose — accept anything starting with \"10.\" after the optional\n// `doi.org/` prefix; OpenAlex itself does the normalization.\nconst DOI_BARE = /^10\\.\\S+$/;\n\nexport function requireString(value, label) {\n    const s = String(value ?? '').trim();\n    if (!s) throw new ArgumentError(`openalex ${label} cannot be empty`);\n    return s;\n}\n\nexport function requireBoundedInt(value, defaultValue, maxValue, label = 'limit') {\n    const raw = value ?? defaultValue;\n    const n = typeof raw === 'number' ? raw : Number(raw);\n    if (!Number.isInteger(n) || n <= 0) {\n        throw new ArgumentError(`openalex ${label} must be a positive integer`);\n    }\n    if (n > maxValue) {\n        throw new ArgumentError(`openalex ${label} must be <= ${maxValue}`);\n    }\n    return n;\n}\n\n/**\n * Resolve a user-supplied work identifier to OpenAlex's canonical path\n * segment. Accepts `W…` IDs, `doi:10.…`, raw DOIs, or full\n * `https://doi.org/…` / `https://openalex.org/W…` URLs.\n */\nexport function requireWorkRef(value) {\n    const raw = String(value ?? '').trim();\n    if (!raw) {\n        throw new ArgumentError('openalex work id is required (e.g. \"W2741809807\", \"10.7717/peerj.4375\")');\n    }\n    // 1) full openalex URL\n    const oaUrl = raw.match(/^https?:\\/\\/(?:api\\.)?openalex\\.org\\/(?:works\\/)?([WAaSCFwIPwT]\\d+)/i);\n    if (oaUrl) {\n        const id = oaUrl[1].toUpperCase();","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/openalex/utils.js#L15-L51","documentation":"requireBoundedInt enforces an upper bound on numeric arguments and throws ArgumentError when the value exceeds `maxValue`. For the OpenAlex adapter this caps `limit` because the API's `per-page` parameter has a maximum page size; larger values would be rejected or misbehave upstream. The message names both the label and the allowed maximum.","triggerScenarios":"Calling a command with `limit` greater than the configured maxValue (e.g. requesting per-page=500 when the adapter caps at 200).","commonSituations":"Assuming the API accepts arbitrarily large page sizes; copying a limit from another API's docs; trying to 'fetch everything' with one huge limit instead of paginating.","solutions":["Lower limit to the documented maximum (see the command's --help or maxValue)","Paginate using multiple requests with cursor/page instead of one oversized request","Clamp the value in your code: Math.min(requested, maxValue)"],"exampleFix":"// before\nawait search({ query, limit: 500 });\n// after\nconst MAX = 200;\nawait search({ query, limit: Math.min(500, MAX) }); // or paginate","handlingStrategy":"validation","validationCode":"const OPENALEX_MAX_LIMIT = 200;\nconst limit = Math.min(Number(rawLimit) || 25, OPENALEX_MAX_LIMIT);","typeGuard":"function isWithinBound(v, max) {\n  return typeof v === 'number' && Number.isInteger(v) && v > 0 && v <= max;\n}","tryCatchPattern":"try {\n  await search({ query, limit });\n} catch (e) {\n  if (e.name === 'ArgumentError' && /must be <=/.test(e.message)) {\n    console.error(`limit exceeds API maximum (${e.message})`); process.exitCode = 2;\n  } else throw e;\n}","preventionTips":["Clamp limits with Math.min against the documented max","Paginate with multiple requests instead of one giant limit","Read the command's --help for the allowed maximum"],"tags":["openalex","argument-validation","numeric-range"],"backgroundTag":"invalid-argument-value","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}