{"record":{"id":"7e9505a0d96f1957","repo":"jackwener/OpenCLI","slug":"upwork-job-id-is-required","errorCode":null,"errorMessage":"upwork job id is required","messagePattern":"upwork job id is required","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/upwork/utils.js","lineNumber":79,"sourceCode":"    }\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;\n}\n\nexport function requireFeedTab(value, defaultValue = 'best-matches') {\n    const v = String(value ?? defaultValue).trim().toLowerCase();\n    if (!FEED_TABS[v]) {\n        throw new ArgumentError(`upwork tab must be one of ${Object.keys(FEED_TABS).join(' / ')}, got \"${value}\"`);\n    }\n    return v;\n}\n\nexport function requireSort(value, defaultValue = 'recency') {\n    const v = String(value ?? defaultValue).trim().toLowerCase();","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/upwork/utils.js#L61-L97","documentation":"requireCiphertext validates Upwork job ids, which must be ciphertext of the form ~01… or ~02… followed by digits. This empty-input branch throws ArgumentError when the id argument is missing, empty, or whitespace after String() coercion and trim().","triggerScenarios":"Calling the detail/show command with no id argument, an empty string '', null/undefined (String(null ?? '') = ''), or a value consisting only of spaces.","commonSituations":"Scripting the detail command from a variable that was never populated; piping an empty row from another command; forgetting the positional argument; shell variable unset ($ID expands to empty).","solutions":["Pass a job id like ~02abc123... as the argument","Verify the upstream variable/pipe actually contains a value before invoking","Capture an id from a list/search command's round-trippable rows first"],"exampleFix":"// before\nconst id = process.env.JOB_ID; // unset -> ''\nawait show(id); // upwork job id is required\n// after\nif (!process.env.JOB_ID) throw new Error('JOB_ID not set');\nawait show(process.env.JOB_ID);","handlingStrategy":"validation","validationCode":"function hasJobId(v) {\n  return typeof v === 'string' && v.trim().length > 0;\n}\nif (!hasJobId(id)) throw new Error('job id required before calling detail');","typeGuard":"function isNonEmptyString(v) {\n  return typeof v === 'string' && v.trim().length > 0;\n}","tryCatchPattern":"try {\n  await show(id);\n} catch (e) {\n  if (e instanceof ArgumentError && e.message === 'upwork job id is required') {\n    console.error('JOB_ID env/arg missing');\n    process.exitCode = 2;\n    return;\n  }\n  throw e;\n}","preventionTips":["Check variables are populated before invoking scripted commands","Use set -u in shell scripts to catch unset variables","Obtain ids from list/search output rather than typing them"],"tags":["validation","cli","missing-argument","upwork"],"backgroundTag":"missing-required-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}