{"record":{"id":"e0b6bfed450fa99d","repo":"jackwener/OpenCLI","slug":"invalid-from-username-json-stringify-kwargs-f","errorCode":null,"errorMessage":"Invalid --from username: ${JSON.stringify(kwargs.from)}","messagePattern":"Invalid --from username: (.+?)","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/twitter/search.js","lineNumber":131,"sourceCode":" * unit coverage.\n *\n * Behaviour notes:\n * - Trims leading `@` from --from so callers can pass `@alice` or `alice`.\n * - Order is `<query> from:X filter:Y -filter:Z` (matches what X's own search\n *   bar emits when you click the suggestions UI).\n * - Empty <query> with non-empty filters is allowed — the resulting string\n *   is just the operator clauses joined; X handles that fine.\n *\n * @param {string} rawQuery\n * @param {{ from?: string, has?: string, exclude?: string }} kwargs\n * @returns {string}\n */\nfunction buildSearchQuery(rawQuery, kwargs) {\n    const parts = [String(rawQuery ?? '').trim()];\n    if (kwargs.from) {\n        const fromUser = String(kwargs.from).trim().replace(/^@+/, '');\n        if (fromUser && !FROM_USER_PATTERN.test(fromUser)) {\n            throw new ArgumentError(\n                `Invalid --from username: ${JSON.stringify(kwargs.from)}`,\n                'Use a Twitter/X handle with 1-15 letters, numbers, or underscores; omit @ or pass @handle.',\n            );\n        }\n        if (fromUser) parts.push(`from:${fromUser}`);\n    }\n    if (kwargs.has) {\n        parts.push(`filter:${kwargs.has}`);\n    }\n    if (kwargs.exclude) {\n        const op = EXCLUDE_TO_OPERATOR[kwargs.exclude];\n        if (op) parts.push(op);\n    }\n    return parts.filter(Boolean).join(' ');\n}\n\n/**\n * Resolve which X search tab (`f=` URL param) to land on. `--product` wins","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/search.js#L113-L149","documentation":"ArgumentError from buildSearchQuery when the --from value does not match FROM_USER_PATTERN (a Twitter/X handle of 1-15 letters, digits, or underscores, optionally prefixed with @). The library rejects it before building the query to avoid sending an invalid from: operator to X's search.","triggerScenarios":"`opencli twitter search <query> --from <value>` where <value> contains invalid characters (spaces, hyphens, dots, non-Latin chars), is longer than 15 characters, or is empty after stripping leading @ signs.","commonSituations":"Passing a full URL instead of a handle, passing a display name with spaces ('Jack Dorsey'), typos like '--from jack-dorsey', or shell quoting issues splitting the handle.","solutions":["Use only 1-15 letters, numbers, or underscores for the handle","Strip the @ prefix (it is allowed but optional): --from @jack or --from jack","Remove any URL, display name, or extra characters and pass only the raw handle"],"exampleFix":"// before\nopencli twitter search cli --from 'Jack Dorsey'\n// after\nopencli twitter search cli --from jack","handlingStrategy":"validation","validationCode":"const FROM_USER_PATTERN = /^[A-Za-z0-9_]{1,15}$/;\nfunction validateFromUser(v) {\n  const h = String(v ?? '').trim().replace(/^@+/, '');\n  return h.length > 0 && FROM_USER_PATTERN.test(h);\n}\nif (kwargs.from && !validateFromUser(kwargs.from)) throw new Error('bad --from handle');","typeGuard":"function isValidHandle(v) { return /^[A-Za-z0-9_]{1,15}$/.test(String(v ?? '').trim().replace(/^@+/, '')); }","tryCatchPattern":"try {\n  await opencli.twitter.search(q, { from: handle });\n} catch (e) {\n  if (e.name === 'ArgumentError' && /--from/.test(e.message)) {\n    console.error('Handle must be 1-15 letters/digits/underscores, @ optional');\n  } else throw e;\n}","preventionTips":["Store handles without display names or URLs","Strip @ programmatically before passing","Enforce /^[A-Za-z0-9_]{1,15}$/ in your own input layer","Avoid shell quoting mistakes that inject spaces"],"tags":["validation","argument","twitter","search"],"backgroundTag":"invalid-argument-value","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}