jackwener/OpenCLI · error · ArgumentError
twitter followers user must be a valid Twitter/X handle
Error message
twitter followers user must be a valid Twitter/X handle
What it means
The library normalizes the user argument with normalizeTwitterScreenName; if a non-empty user string was given but normalization produced nothing, the value is not a valid Twitter/X handle. This guards against typos, embedded URLs, or values with illegal characters being sent to the API. It throws immediately as an ArgumentError with an example in the hint.
Solutions
- Pass only the handle, with or without a single leading @, e.g. @elonmusk or elonmusk
- Strip URL parts first: const handle = url.split('/').pop().split('?')[0]
- Validate with /^@?[A-Za-z0-9_]{1,15}$/ before calling
Example fix
// before opencli twitter followers "https://x.com/elonmusk" // after opencli twitter followers @elonmusk --limit 100
Defensive patterns
Strategy: validation
Validate before calling
const HANDLE_RE = /^@?[A-Za-z0-9_]{1,15}$/;
if (!HANDLE_RE.test(rawUser)) throw new Error('not a valid X handle: ' + rawUser); Type guard
const isValidHandle = (s) => typeof s === 'string' && /^@?[A-Za-z0-9_]{1,15}$/.test(s.trim()); Try / catch
try {
await opencli.twitter.followers(user);
} catch (err) {
if (err.message.includes('must be a valid Twitter/X handle')) {
user = extractHandleFromUrl(user); // take last path segment
} else throw err;
} Prevention
- Pass bare handles, never profile URLs
- Trim whitespace and strip a single leading @ before calling
- Validate with the handle regex upstream in scripts
When it happens
Trigger: Passing user values like 'https://x.com/elonmusk', '@@double@', 'invalid name!', a full profile URL with query params, or any string with characters outside the allowed handle alphabet after trimming.
Common situations: Pasting a full profile URL instead of the handle; including trailing slashes or query strings; copying '@' plus whitespace; programmatic callers passing a display name instead of the handle.
Understand the failure class
Background: "must be a positive integer", "cannot be empty", "invalid argument": how invalid-argument errors work across open-source libraries — this error's family across 33 libraries.
Related errors
- account must be an integer between 0 and 20
- --adults must be an integer between 1 and 9, got
- Argument "media" is required.
- arxiv must be a positive integer
- arxiv must be <=
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/5629143ac457a77e.
Report an issue: GitHub.
Appendix: source
Thrown at clis/twitter/followers.js:102
required: false,
help: 'Twitter/X handle (with or without @). Omit to fetch followers of the currently logged-in account.',
},
{ name: 'limit', type: 'int', default: 50, help: 'Maximum number of follower rows to return (default 50). Must be a positive integer.' },
],
// Preserve the historical three-column contract even though the GraphQL
// payload also contains per-user relationship counts. Use `twitter profile`
// when a dedicated follower count is needed.
columns: ['screen_name', 'name', 'bio'],
func: async (page, kwargs) => {
const limit = kwargs.limit;
if (!Number.isInteger(limit) || limit <= 0) {
throw new ArgumentError('limit must be a positive integer');
}
const rawUser = String(kwargs.user ?? '').trim();
let targetUser = normalizeScreenName(rawUser);
if (rawUser && !targetUser) {
throw new ArgumentError('twitter followers user must be a valid Twitter/X handle', 'Example: opencli twitter followers @elonmusk --limit 100');
}
await page.goto('https://x.com/home');
await page.wait({ selector: '[data-testid="primaryColumn"]' });
const cookies = await page.getCookies({ url: 'https://x.com' });
const ct0 = cookies.find((cookie) => cookie.name === 'ct0')?.value || null;
if (!ct0) {
throw new AuthRequiredError('x.com', 'Not logged into x.com (no ct0 cookie)');
}
if (!targetUser) {
// Bridge wraps primitive page.evaluate returns as { session, data:<value> };
// unwrap so the href string is usable downstream.
const href = unwrapBrowserResult(await page.evaluate(`() => {
const link = document.querySelector('a[data-testid="AppTabBar_Profile_Link"]');
return link ? link.getAttribute('href') : null;
}`));
if (!href || typeof href !== 'string') {
throw new AuthRequiredError('x.com', 'Could not find logged-in user profile link. Are you logged in?');View on GitHub (pinned to 49907e53dc)