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.

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)

Solutions

  1. Pass only the handle, with or without a single leading @, e.g. @elonmusk or elonmusk
  2. Strip URL parts first: const handle = url.split('/').pop().split('?')[0]
  3. 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

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.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/5629143ac457a77e. Report an issue: GitHub.