{"record":{"id":"e23045f8e12cb8d3","repo":"jackwener/OpenCLI","slug":"archive-search-limit-must-be-a-positive-integer","errorCode":null,"errorMessage":"archive search limit must be a positive integer","messagePattern":"archive search limit must be a positive integer","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/archive/search.js","lineNumber":40,"sourceCode":"    args: [\n        { name: 'query', positional: true, required: true, help: 'Full-text query (matches title, description, creator, subject).' },\n        { name: 'mediatype', type: 'string', required: false, help: `Restrict to mediatype: ${MEDIATYPES.join(', ')}` },\n        { name: 'sort', type: 'string', default: 'downloads', help: `Sort key: ${SORT_OPTIONS.join(', ')}` },\n        { name: 'limit', type: 'int', default: 20, help: 'Max items (max 100; one API page).' },\n    ],\n    columns: ['rank', 'identifier', 'title', 'creator', 'date', 'mediatype', 'downloads', 'url'],\n    func: async (args) => {\n        const sortRaw = String(args.sort ?? 'downloads').toLowerCase();\n        const sort = SORT_ALIAS[sortRaw] ?? sortRaw;\n        if (!SORT_OPTIONS.includes(sort)) {\n            throw new ArgumentError(`archive search sort must be one of ${SORT_OPTIONS.join(', ')}`);\n        }\n        if (args.mediatype && !MEDIATYPES.includes(String(args.mediatype))) {\n            throw new ArgumentError(`archive search mediatype must be one of ${MEDIATYPES.join(', ')}`);\n        }\n        const limit = Number(args.limit ?? 20);\n        if (!Number.isInteger(limit) || limit <= 0) {\n            throw new ArgumentError('archive search limit must be a positive integer');\n        }\n        if (limit > 100) {\n            throw new ArgumentError('archive search limit must be <= 100');\n        }\n\n        const query = String(args.query ?? '').trim();\n        if (!query) {\n            throw new ArgumentError('archive search query must not be empty');\n        }\n\n        const fullQuery = args.mediatype\n            ? `(${query}) AND mediatype:${args.mediatype}`\n            : query;\n\n        const url = new URL('https://archive.org/advancedsearch.php');\n        url.searchParams.set('q', fullQuery);\n        url.searchParams.set('output', 'json');\n        url.searchParams.set('rows', String(limit));","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/archive/search.js#L22-L58","documentation":"This ArgumentError is thrown before any network call by `archive search` when `limit` is not a positive integer. The value is coerced with Number() and must pass Number.isInteger and be > 0, so NaN, floats, zero, negatives, and non-numeric strings all fail. It is a client-side input validation error.","triggerScenarios":"Calling `opencli archive search <query> --limit 0`, --limit -5, --limit 12.5, --limit abc, or --limit with an empty value that coerces to NaN.","commonSituations":"Passing a shell variable that is unset/empty so limit becomes NaN; assuming 0 means 'unlimited'; locale-formatted numbers like '1,000' or '1.000' that fail integer parsing; copying fractional page sizes from other APIs.","solutions":["Pass a whole number >= 1, e.g. --limit 20","Remove the --limit flag to use the default of 20","Check the calling script for empty/unset variables feeding --limit"],"exampleFix":"// before\nopencli archive search \"ada lovelace\" --limit \"${LIMIT}\"  # LIMIT unset -> NaN\n// after\nLIMIT=\"${LIMIT:-20}\"\nopencli archive search \"ada lovelace\" --limit \"$LIMIT\"","handlingStrategy":"validation","validationCode":"const limit = Number(rawLimit ?? 20);\nif (!Number.isInteger(limit) || limit <= 0) {\n    throw new Error(`limit must be a positive integer, got ${rawLimit}`);\n}","typeGuard":"const isValidLimit = (n) => Number.isInteger(n) && n > 0;","tryCatchPattern":"try {\n    rows = await run(['archive', 'search', query, '--limit', String(limit)]);\n} catch (err) {\n    if (err instanceof ArgumentError && err.message.includes('limit must be a positive integer')) {\n        rows = await run(['archive', 'search', query]); // default limit 20\n    } else { throw err; }\n}","preventionTips":["Coerce and validate numeric args with Number.isInteger before passing them","Guard shell scripts against unset variables feeding --limit","Never use 0 as 'unlimited' — use the max of 100 instead","Log the coerced value so NaN from empty strings is visible"],"tags":["validation","cli","argument-error","input"],"backgroundTag":"invalid-argument-value","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}