{"record":{"id":"c906a51cb4d725db","repo":"jackwener/OpenCLI","slug":"boolean-argument-name-must-be-true-or-false","errorCode":null,"errorMessage":"Boolean argument ${name} must be true or false","messagePattern":"Boolean argument (.+?) must be true or false","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/mercury/utils.js","lineNumber":29,"sourceCode":"        throw new ArgumentError(`Missing required argument: ${name}`);\n    }\n    return value.trim();\n}\n\nexport function optionalString(kwargs, name, fallback) {\n    const value = kwargs[name];\n    if (typeof value !== 'string' || value.trim() === '') return fallback;\n    return value.trim();\n}\n\nexport function optionalBoolean(kwargs, name, fallback = false) {\n    const value = kwargs[name];\n    if (typeof value === 'boolean') return value;\n    if (typeof value === 'string') {\n        const normalized = value.trim().toLowerCase();\n        if (['1', 'true', 'yes', 'y', 'on'].includes(normalized)) return true;\n        if (['0', 'false', 'no', 'n', 'off'].includes(normalized)) return false;\n        throw new ArgumentError(`Boolean argument ${name} must be true or false`);\n    }\n    return fallback;\n}\n\nfunction parseReceiptPath(kwargs) {\n    const receipt = path.resolve(requireString(kwargs, 'receipt'));\n    let stat;\n    try {\n        stat = fs.statSync(receipt, { throwIfNoEntry: false });\n    }\n    catch (error) {\n        throw new ArgumentError(`Receipt file cannot be read: ${receipt}`, error instanceof Error ? error.message : undefined);\n    }\n    if (!stat || !stat.isFile()) {\n        throw new ArgumentError(`Receipt file does not exist: ${receipt}`);\n    }\n    return receipt;\n}","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/mercury/utils.js#L11-L47","documentation":"optionalBoolean parses a boolean argument leniently: real booleans pass through, strings like 'true'/'yes'/'1'/'on' become true, 'false'/'no'/'0'/'off' become false, and anything else falls back to the default. If the value is a string but not in either accepted list, it throws ArgumentError telling the caller the argument must be true or false.","triggerScenarios":"Calling normalizeReimbursementInput with a boolean-ish argument (e.g. closeAfterReview) whose string value is not one of 1/true/yes/y/on/0/false/no/n/off — e.g. 'maybe', 'enabled', '2', or 't' throws; casing is normalized so 'TRUE' is fine.","commonSituations":"Config file uses 'enabled'/'disabled' instead of the accepted tokens; user passes an unlisted variant like 'yeah'; a wrapper converts booleans to unexpected words; localized flag values in scripts.","solutions":["Change the value to one of the accepted strings: 1, true, yes, y, on, 0, false, no, n, off (case-insensitive).","Prefer passing a real boolean (true/false) instead of a string when calling programmatically.","Omit the argument entirely if you want the default — optionalBoolean returns the fallback for undefined values.","Update wrapper scripts/configs to emit canonical boolean strings."],"exampleFix":"// before\nawait draftReimbursement({ receipt: 'r.pdf', amount: '10', date: '2026-08-29', closeAfterReview: 'enabled' });\n// after\nawait draftReimbursement({ receipt: 'r.pdf', amount: '10', date: '2026-08-29', closeAfterReview: 'true' });","handlingStrategy":"validation","validationCode":"const BOOL_TRUE = ['1','true','yes','y','on'];\nconst BOOL_FALSE = ['0','false','no','n','off'];\nfunction checkBoolArg(kwargs, name) {\n    const v = kwargs[name];\n    if (v === undefined || typeof v === 'boolean') return;\n    if (typeof v === 'string') {\n        const n = v.trim().toLowerCase();\n        if (BOOL_TRUE.includes(n) || BOOL_FALSE.includes(n)) return;\n    }\n    throw new Error(`Boolean argument ${name} must be true or false`);\n}\ncheckBoolArg(input, 'closeAfterReview');","typeGuard":"function isBoolLike(v) {\n    if (typeof v === 'boolean' || v === undefined) return true;\n    if (typeof v !== 'string') return false;\n    const n = v.trim().toLowerCase();\n    return ['1','true','yes','y','on','0','false','no','n','off'].includes(n);\n}","tryCatchPattern":"try {\n    await draftReimbursement(input);\n} catch (err) {\n    if (err instanceof ArgumentError && err.message.includes('must be true or false')) {\n        console.error('Invalid boolean value; use true/false (also: 1/0, yes/no, on/off)');\n        process.exitCode = 2;\n        return;\n    }\n    throw err;\n}","preventionTips":["Use real booleans when calling programmatically instead of strings.","Normalize config values ('enabled' -> 'true') before passing them.","Omit optional boolean flags entirely to get the default.","Keep a shared helper for boolean parsing across scripts."],"tags":["argument-validation","cli","boolean-parsing"],"backgroundTag":"invalid-argument-value","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}