{"record":{"id":"a9e0afe029a76c91","repo":"jackwener/OpenCLI","slug":"weread-official-label-must-be-one-of-choice","errorCode":null,"errorMessage":"weread-official: ${label} must be one of: ${choices.join(', ')}","messagePattern":"weread-official: (.+?) must be one of: (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/weread-official/utils.js","lineNumber":283,"sourceCode":"    }\n    const text = String(value).trim();\n    if (!/^\\d+$/.test(text)) {\n        throw new ArgumentError(`weread-official: ${label} must be a positive integer`);\n    }\n    const n = Number(text);\n    if (!Number.isSafeInteger(n) || n < 1) {\n        throw new ArgumentError(`weread-official: ${label} must be a positive integer`);\n    }\n    if (max !== undefined && n > max) {\n        throw new ArgumentError(`weread-official: ${label} must be <= ${max}`);\n    }\n    return n;\n}\n\nexport function requireChoice(value, choices, label, defaultValue) {\n    const text = String(value ?? defaultValue ?? '').trim();\n    if (!choices.includes(text)) {\n        throw new ArgumentError(`weread-official: ${label} must be one of: ${choices.join(', ')}`);\n    }\n    return text;\n}\n\n// ── Empty-result helper ────────────────────────────────────────────────────\n\n/** Throw EmptyResultError with a stable command label. */\nexport function emptyResult(command, hint) {\n    throw new EmptyResultError(`weread-official ${command}`, hint);\n}\n","sourceCodeStart":265,"sourceCodeEnd":294,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/weread-official/utils.js#L265-L294","documentation":"requireChoice validates that a value is one of an enumerated set of allowed strings; if the trimmed text (after applying any defaultValue) is not in the choices array, it throws this ArgumentError listing the valid options. It is used by mode-type options to guarantee only supported modes reach the API layer.","triggerScenarios":"Passing --mode something to the mode option where 'something' is not in the choices array, or passing an empty value with no default configured.","commonSituations":"Misspelling a mode ('daily' vs 'day'); case mismatch ('Daily' vs 'daily' — matching is exact); using a mode removed or renamed in a newer CLI version.","solutions":["Use exactly one of the listed choices from the error message, respecting case.","Run the command with --help to see the current set of accepted modes.","If upgrading from an older version, map the renamed mode to its new name.","In scripts, validate against the same list before invoking, or pick from the choices programmatically."],"exampleFix":"// before\ncli(['recommend', '--mode', 'Daily']);\n// after\ncli(['recommend', '--mode', 'daily']); // exact match of a listed choice","handlingStrategy":"validation","validationCode":"const MODES = ['daily', 'weekly', 'monthly']; // match the command's choices\nif (!MODES.includes(mode)) throw new Error(`mode must be one of: ${MODES.join(', ')}`);","typeGuard":"function isValidMode(v, choices) { return choices.includes(String(v ?? '').trim()); }","tryCatchPattern":"try {\n  await cli.run(['recommend', '--mode', mode]);\n} catch (e) {\n  if (e instanceof ArgumentError && /must be one of:/.test(e.message)) {\n    const choices = e.message.split('must be one of: ')[1].split(', ');\n    console.error(`Invalid mode '${mode}'. Valid: ${choices.join(', ')}`); process.exitCode = 2;\n  } else throw e;\n}","preventionTips":["Parse the allowed choices from the error message and validate interactively.","Never hardcode mode strings copied from old scripts; verify against --help after upgrades.","Keep enum values in a shared constant module used by both scripts and docs.","Remember matching is exact and case-sensitive."],"tags":["argument-validation","cli","enum"],"backgroundTag":"invalid-enum-value","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}