{"record":{"id":"0d5e7a21f31cb5a2","repo":"jackwener/OpenCLI","slug":"steam-label-must-be-a-positive-integer","errorCode":null,"errorMessage":"steam ${label} must be a positive integer","messagePattern":"steam (.+?) must be a positive integer","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/steam/utils.js","lineNumber":32,"sourceCode":"}\n\nexport function requireCountryCode(value, defaultValue = 'us') {\n    const raw = value === undefined || value === null ? defaultValue : value;\n    const code = String(raw).trim().toLowerCase();\n    if (!/^[a-z]{2}$/.test(code)) {\n        throw new ArgumentError(\n            `steam currency must be a two-letter storefront country code (got \"${value}\")`,\n            'Examples: us, cn, jp, de. This controls Steam regional pricing and availability.',\n        );\n    }\n    return code;\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(`steam ${label} must be a positive integer`);\n    }\n    if (n > maxValue) {\n        throw new ArgumentError(`steam ${label} must be <= ${maxValue}`);\n    }\n    return n;\n}\n\nexport function requireAppId(value) {\n    const s = String(value ?? '').trim();\n    if (!s) {\n        throw new ArgumentError('steam app id is required (e.g. \"620\" for Portal 2)');\n    }\n    if (!/^\\d+$/.test(s)) {\n        throw new ArgumentError(\n            `steam app id \"${value}\" must be a positive integer`,\n            'Copy the numeric id from `steam search` or the URL `store.steampowered.com/app/<id>/`.',\n        );\n    }","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/steam/utils.js#L14-L50","documentation":"requireBoundedInt coerces its input to a number and requires a positive integer; anything else (floats, zero, negatives, non-numeric strings, NaN) throws ArgumentError. It protects numeric options like limit from bad input.","triggerScenarios":"Passing --limit 0, --limit -5, --limit abc, --limit 12.5, or an empty string that coerces to NaN; default path is fine because the default is applied when value is undefined/null.","commonSituations":"Users passing 'all' or blank values to a limit flag; shell variables that are unset producing empty strings; JSON config carrying null → default applies, but \"\" does not.","solutions":["Pass a positive integer value for the option (e.g. --limit 20)","Pre-parse and validate numeric flags before calling the command","Ensure empty strings are converted to undefined so the default is used","Catch ArgumentError to display correct flag usage"],"exampleFix":"// before\n--limit \"\"   // Number(\"\") === 0 → ArgumentError\n// after\n--limit 20","handlingStrategy":"validation","validationCode":"function asPositiveInt(v) {\n  const n = typeof v === 'number' ? v : Number(String(v ?? '').trim());\n  if (!Number.isInteger(n) || n <= 0) throw new Error(`expected positive integer, got: ${v}`);\n  return n;\n}","typeGuard":"function isPositiveInt(v) {\n  return typeof v === 'number' && Number.isInteger(v) && v > 0;\n}","tryCatchPattern":"try {\n  await cmd({ limit: raw });\n} catch (e) {\n  if (e instanceof ArgumentError && /must be a positive integer/.test(e.message)) {\n    console.error('Pass an integer like --limit 20');\n  } else throw e;\n}","preventionTips":["Parse numeric flags with Number.parseInt and check isNaN at the CLI boundary","Treat empty-string flags as unset so defaults apply","Document valid ranges in help text","Write tests for zero, negative, and non-numeric inputs"],"tags":["validation","argument","steam"],"backgroundTag":"invalid-argument-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}