{"record":{"id":"5c1f78da722dacb9","repo":"jackwener/OpenCLI","slug":"twitter-commandname-username-must-be-a-valid-tw","errorCode":null,"errorMessage":"twitter ${commandName} username must be a valid Twitter/X handle","messagePattern":"twitter (.+?) username must be a valid Twitter/X handle","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/twitter/user-timeline.js","lineNumber":162,"sourceCode":"    if (cursor) vars.cursor = cursor;\n    return appendGraphqlParams(`/i/api/graphql/${normalized.queryId}/UserTweets`, vars, normalized);\n}\n\nexport function buildUserByScreenNameUrl(operation, screenName) {\n    const normalized = normalizeUserByScreenNameOperation(operation);\n    const vars = { screen_name: screenName, withSafetyModeUserFields: true };\n    return appendGraphqlParams(`/i/api/graphql/${normalized.queryId}/UserByScreenName`, vars, normalized);\n}\n\nexport async function resolveUserTimelineContext(\n    page,\n    rawUsername,\n    { allowLoggedInDefault = false, commandName = 'tweets' } = {},\n) {\n    const raw = String(rawUsername ?? '').trim();\n    let username = normalizeTwitterScreenName(raw);\n    if (raw && !username) {\n        throw new ArgumentError(\n            `twitter ${commandName} username must be a valid Twitter/X handle`,\n            commandName === 'collection'\n                ? 'Example: opencli twitter collection @jack --until 2026-07-23T00:00:00Z'\n                : 'Example: opencli twitter tweets @jack --limit 20',\n        );\n    }\n    if (!username && !allowLoggedInDefault) {\n        throw new ArgumentError('twitter collection username must be a valid Twitter/X handle', 'Example: opencli twitter collection @jack --until 2026-07-23T00:00:00Z');\n    }\n    if (!username) {\n        await page.goto('https://x.com/home');\n        await page.wait({ selector: '[data-testid=\"primaryColumn\"]' });\n        const href = unwrapBrowserResult(await page.evaluate(`() => {\n            const link = document.querySelector('a[data-testid=\"AppTabBar_Profile_Link\"]');\n            return link ? link.getAttribute('href') : null;\n        }`));\n        if (!href || typeof href !== 'string') {\n            throw new AuthRequiredError('x.com', 'Could not detect logged-in user. Are you logged in?');","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/user-timeline.js#L144-L180","documentation":"`resolveUserTimelineContext` normalizes the raw username argument with `normalizeTwitterScreenName`. If a non-empty raw value was supplied but normalization yields an empty string, it throws this ArgumentError naming the command (`tweets`, `collection`, etc.). The library requires a syntactically valid Twitter/X handle for every timeline-style command; it refuses to guess from a malformed input.","triggerScenarios":"Passing something like `https://x.com/jack`, `@`, `jack!`, a full URL, or whitespace-plus-symbols as the username argument to a twitter command routed through resolveUserTimelineContext with commandName not equal to 'collection'.","commonSituations":"Users pasting a profile URL where a handle is expected; stray characters or quotes around the handle; Windows shell mangling `@` or `%` characters; forgetting that only bare screen names (with optional leading @) are accepted.","solutions":["Pass a bare handle, with or without a leading @: `opencli twitter tweets @jack --limit 20`.","Strip the URL down to the screen name if you copied a profile link (x.com/jack -> jack).","Quote the argument in your shell if it contains special characters.","Check for invisible characters/whitespace pasted from a document; retype the handle."],"exampleFix":"// before\nopencli twitter tweets https://x.com/jack --limit 20\n// after\nopencli twitter tweets @jack --limit 20","handlingStrategy":"validation","validationCode":"const SCREEN_NAME_RE = /^[A-Za-z0-9_]{1,15}$/;\nfunction toHandle(raw) {\n  const s = String(raw ?? '').trim().replace(/^@/, '');\n  if (!SCREEN_NAME_RE.test(s)) {\n    throw new Error(`'${raw}' is not a valid Twitter/X handle; pass a bare name like @jack`);\n  }\n  return s;\n}\n// use: opencli twitter tweets ${toHandle(input)} --limit 20","typeGuard":"function isValidHandle(raw) {\n  return /^[A-Za-z0-9_]{1,15}$/.test(String(raw ?? '').trim().replace(/^@/, ''));\n}","tryCatchPattern":"try {\n  await cli.run(['twitter', 'tweets', username, '--limit', '20']);\n} catch (e) {\n  if (/must be a valid Twitter\\/X handle/.test(e.message)) {\n    console.error('Extract the screen name from URLs first, e.g. x.com/jack -> jack');\n  } else throw e;\n}","preventionTips":["Always pass a bare handle (optional leading @), never a profile URL.","Quote arguments containing @ or special characters in your shell.","Sanitize inputs extracted from URLs before passing them.","Retype pasted handles to eliminate invisible characters."],"tags":["argument-validation","twitter","username"],"backgroundTag":"invalid-argument-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}