{"record":{"id":"aa736c5a4373fb16","repo":"jackwener/OpenCLI","slug":"invalid-twitter-x-username-json-stringify-usern","errorCode":null,"errorMessage":"Invalid Twitter/X username: ${JSON.stringify(username)}","messagePattern":"Invalid Twitter/X username: (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/twitter/list-batch-utils.js","lineNumber":26,"sourceCode":"    const raw = String(rawValue || '').trim();\n    if (!raw) {\n        throw new ArgumentError('At least one username is required', example);\n    }\n\n    const values = raw\n        .split(',')\n        .map((part) => part.trim().replace(/^@/, ''))\n        .filter(Boolean);\n\n    if (values.length === 0) {\n        throw new ArgumentError('At least one username is required', example);\n    }\n\n    const seen = new Set();\n    const usernames = [];\n    for (const username of values) {\n        if (!USERNAME_RE.test(username)) {\n            throw new ArgumentError(`Invalid Twitter/X username: ${JSON.stringify(username)}`, example);\n        }\n        const key = username.toLowerCase();\n        if (seen.has(key)) continue;\n        seen.add(key);\n        usernames.push(username);\n    }\n\n    return usernames;\n}\n\nexport function parseBatchIntervalSeconds(rawValue) {\n    const value = rawValue === undefined || rawValue === null || rawValue === ''\n        ? DEFAULT_INTERVAL_SECONDS\n        : Number(rawValue);\n    if (!Number.isInteger(value) || value < 0 || value > MAX_INTERVAL_SECONDS) {\n        throw new ArgumentError(`Invalid interval: ${JSON.stringify(rawValue)}. Expected an integer from 0 to ${MAX_INTERVAL_SECONDS}.`);\n    }\n    return value;","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/list-batch-utils.js#L8-L44","documentation":"Each comma-separated username must match USERNAME_RE = /^[A-Za-z0-9_]{1,15}$/ — X handles are 1–15 chars of letters, digits, and underscores. If any part fails, parseCommaSeparatedUsernames throws ArgumentError quoting the offending value via JSON.stringify. The full batch is rejected rather than partially run.","triggerScenarios":"Passing a part containing invalid characters (spaces, hyphens, dots, non-ASCII), longer than 15 chars, or an accidental embedded value like \"alice, bob smith\" — 'bob smith' fails the regex.","commonSituations":"Including full profile URLs (https://x.com/alice) instead of handles; pasting display names instead of handles; trailing punctuation from prose (\"alice.\"); handles longer than 15 chars (old 20-char legacy names or typos); non-Latin scripts.","solutions":["Use bare handles only: letters/digits/underscore, max 15 chars — strip URLs and display names from your input.","Remove spaces around entries (trailing punctuation counts as part of the name after trim).","Verify each handle on x.com; rename >15-char entries to their current valid handle.","Pre-validate with /^[A-Za-z0-9_]{1,15}$/ in your tooling before invoking the CLI."],"exampleFix":"// before\nopencli twitter list-batch-add 123456789 --usernames \"https://x.com/alice,Bob Smith\"\nArgumentError: Invalid Twitter/X username: \"Bob Smith\"\n// after\nopencli twitter list-batch-add 123456789 --usernames \"alice,bob_smith\"","handlingStrategy":"validation","validationCode":"const USERNAME_RE = /^[A-Za-z0-9_]{1,15}$/;\nconst bad = raw.split(',').map((p) => p.trim().replace(/^@/, '')).filter((p) => p && !USERNAME_RE.test(p));\nif (bad.length) throw new Error(`Invalid handles: ${bad.join(', ')} — use 1-15 chars of A-Za-z0-9_`);","typeGuard":"function isValidHandle(u) {\n  return typeof u === 'string' && /^[A-Za-z0-9_]{1,15}$/.test(u);\n}","tryCatchPattern":"try {\n  await runBatch(argv);\n} catch (e) {\n  if (e instanceof ArgumentError && /Invalid Twitter\\/X username/.test(e.message)) {\n    console.error(`${e.message} — strip URLs/display names; handles are 1-15 chars of letters, digits, underscore.`);\n    return;\n  }\n  throw e;\n}","preventionTips":["Normalize input: strip URLs (https://x.com/) and @ prefixes, keep bare handles.","Reject display names and prose-pasted values in your tooling.","Validate each entry against /^[A-Za-z0-9_]{1,15}$/ before invoking the CLI.","Deduplicate case-insensitively — the parser already does."],"tags":["argument-validation","regex","cli","input"],"backgroundTag":"invalid-username-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}