{"record":{"id":"b056dee7fadabc8f","repo":"jackwener/OpenCLI","slug":"upwork-label-must-be-min","errorCode":null,"errorMessage":"upwork ${label} must be >= ${min}","messagePattern":"upwork (.+?) must be >= (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/upwork/utils.js","lineNumber":67,"sourceCode":"\nexport function requireQuery(value, label = 'query') {\n    const q = String(value ?? '').trim();\n    if (!q) throw new ArgumentError(`upwork ${label} cannot be empty`);\n    return q;\n}\n\nexport function requirePositiveInt(value, defaultValue, label) {\n    const raw = value ?? defaultValue;\n    const n = coerceInt(raw);\n    if (!Number.isInteger(n) || n <= 0) {\n        throw new ArgumentError(`upwork ${label} must be a positive integer`);\n    }\n    return n;\n}\n\nexport function requireBoundedInt(value, defaultValue, min, max, label) {\n    const n = requirePositiveInt(value, defaultValue, label);\n    if (n < min) throw new ArgumentError(`upwork ${label} must be >= ${min}`);\n    if (n > max) throw new ArgumentError(`upwork ${label} must be <= ${max}`);\n    return n;\n}\n\n/**\n * Upwork job ids are the ciphertext form starting with `~01` or `~02`\n * (the encoded uid surfaced everywhere in URLs and search results).\n * Accepts a bare ciphertext or a full `/jobs/~02…` URL.\n */\nexport function requireCiphertext(value) {\n    let id = String(value ?? '').trim();\n    if (!id) throw new ArgumentError('upwork job id is required');\n    const urlMatch = id.match(/~0[12]\\d+/);\n    if (urlMatch) id = urlMatch[0];\n    if (!CIPHERTEXT_PATTERN.test(id)) {\n        throw new ArgumentError(`upwork job id \"${value}\" is not a valid ciphertext (expected ~01… or ~02… followed by digits)`);\n    }\n    return id;","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/upwork/utils.js#L49-L85","documentation":"requireBoundedInt validates an integer CLI option and enforces both a minimum and maximum bound. The lower-bound check throws this ArgumentError when the resolved value is below `min`. It guards options like limit/page sizes from values the API or paging logic cannot honor.","triggerScenarios":"Calling a command with an option routed through requireBoundedInt (e.g. `limit`) whose value, after requirePositiveInt coercion, is numerically less than the configured min (e.g. limit=0 or a negative number, since 0 and negatives pass requirePositiveInt only if >0 — so realistically limit set below the min bound such as min=10 with limit=5).","commonSituations":"Passing --limit 0 or a small value like 1 when the command requires a minimum of several results; copy-pasting a limit from another CLI with different bounds; setting limits via config/env with stale small values.","solutions":["Raise the option value to at least the documented minimum shown in the error message","Omit the option to fall back to the defaultValue passed to requireBoundedInt","Check the command's --help for the valid min/max range for that option"],"exampleFix":"// before\n$ opencli upwork search \"vue\" --limit 5\n// error: upwork limit must be >= 10\n// after\n$ opencli upwork search \"vue\" --limit 10","handlingStrategy":"validation","validationCode":"const n = Number(limit);\nif (!Number.isInteger(n) || n < min || n > max) throw new RangeError(`limit must be between ${min} and ${max}`);","typeGuard":"function isBoundedInt(v, min, max) {\n  return Number.isInteger(v) && v >= min && v <= max;\n}","tryCatchPattern":"try {\n  await cmd({ limit });\n} catch (e) {\n  if (e instanceof ArgumentError && /must be >=/.test(e.message)) {\n    limit = DEFAULT_LIMIT;\n    return cmd({ limit });\n  }\n  throw e;\n}","preventionTips":["Check --help for min/max bounds before scripting options","Clamp values with Math.min/Math.max before passing","Prefer omitting options to use sane defaults"],"tags":["validation","cli","argument-error","upwork"],"backgroundTag":"option-out-of-range","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}