{"record":{"id":"b61488408035d609","repo":"jackwener/OpenCLI","slug":"rubygems-label-must-be-maxvalue","errorCode":null,"errorMessage":"rubygems ${label} must be <= ${maxValue}","messagePattern":"rubygems (.+?) must be <= (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/rubygems/utils.js","lineNumber":28,"sourceCode":"const UA = 'opencli-rubygems-adapter (+https://github.com/jackwener/opencli)';\n\n// RubyGems gem name pattern (mirrors the rubygems-server validation).\nconst GEM_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;\n\nexport function requireString(value, label) {\n    const s = String(value ?? '').trim();\n    if (!s) throw new ArgumentError(`rubygems ${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(`rubygems ${label} must be a positive integer`);\n    }\n    if (n > maxValue) {\n        throw new ArgumentError(`rubygems ${label} must be <= ${maxValue}`);\n    }\n    return n;\n}\n\nexport function requireGemName(value) {\n    const s = String(value ?? '').trim();\n    if (!s) {\n        throw new ArgumentError('rubygems gem name is required (e.g. \"rails\", \"sidekiq\")');\n    }\n    if (s.length > 100 || !GEM_NAME.test(s)) {\n        throw new ArgumentError(\n            `rubygems gem \"${value}\" is not a valid gem name`,\n            'Use letters / digits / \"._-\", starting with a letter or digit (max 100 chars).',\n        );\n    }\n    return s;\n}\n","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/rubygems/utils.js#L10-L46","documentation":"requireBoundedInt also enforces an upper bound: after passing the positive-integer check, a value greater than maxValue throws ArgumentError(`rubygems ${label} must be <= ${maxValue}`). For rubygems search the maxValue is 100, so limit is clamped to the API's sane range rather than letting requests grow unbounded.","triggerScenarios":"search({ query: 'rails', limit: 500 }) or any limit > 100; any other requireBoundedInt caller passing a value above the maxValue it was given.","commonSituations":"Users expecting 'give me everything' via a huge limit; scripts copying limits from other tools with different bounds; misremembering the max (e.g. trying 1000 for a top-100 API).","solutions":["Lower the limit to 100 or below: `--limit 100` is the maximum for rubygems search.","If more results are needed, page through via multiple queries with different terms (the API only exposes page=1 here).","Clamp in calling code: `limit = Math.min(Number(limit) || 30, 100)` before invoking."],"exampleFix":"// before\nawait search({ query: 'rails', limit: 500 }); // throws\n\n// after\nawait search({ query: 'rails', limit: Math.min(500, 100) }); // 100","handlingStrategy":"validation","validationCode":"function clampLimit(v, max = 100, def = 30) {\n  const n = v == null ? def : Number(v);\n  if (!Number.isInteger(n) || n <= 0) throw new Error(`limit must be a positive integer`);\n  if (n > max) throw new Error(`limit must be <= ${max}`);\n  return n;\n}","typeGuard":"function isBoundedInt(v, max) {\n  return Number.isInteger(v) && v > 0 && v <= max;\n}","tryCatchPattern":"try {\n  await search({ query, limit });\n} catch (e) {\n  if (e instanceof ArgumentError && /must be <=/.test(e.message)) {\n    console.error('Max limit is 100 for rubygems search');\n  } else throw e;\n}","preventionTips":["Clamp user-supplied limits to the documented max (100) before calling.","Search cannot page beyond page=1 here — use narrower queries for more results.","Validate bounds at the CLI boundary, not deep in the call."],"tags":["validation","argument-error","cli-input","numeric"],"backgroundTag":"invalid-argument-value","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}