jackwener/OpenCLI · error · ArgumentError

twitter ${commandName} username must be a valid Twitter/X ha

Error message

twitter ${commandName} username must be a valid Twitter/X handle

What it means

`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.

Source

Thrown at clis/twitter/user-timeline.js:162

    if (cursor) vars.cursor = cursor;
    return appendGraphqlParams(`/i/api/graphql/${normalized.queryId}/UserTweets`, vars, normalized);
}

export function buildUserByScreenNameUrl(operation, screenName) {
    const normalized = normalizeUserByScreenNameOperation(operation);
    const vars = { screen_name: screenName, withSafetyModeUserFields: true };
    return appendGraphqlParams(`/i/api/graphql/${normalized.queryId}/UserByScreenName`, vars, normalized);
}

export async function resolveUserTimelineContext(
    page,
    rawUsername,
    { allowLoggedInDefault = false, commandName = 'tweets' } = {},
) {
    const raw = String(rawUsername ?? '').trim();
    let username = normalizeTwitterScreenName(raw);
    if (raw && !username) {
        throw new ArgumentError(
            `twitter ${commandName} username must be a valid Twitter/X handle`,
            commandName === 'collection'
                ? 'Example: opencli twitter collection @jack --until 2026-07-23T00:00:00Z'
                : 'Example: opencli twitter tweets @jack --limit 20',
        );
    }
    if (!username && !allowLoggedInDefault) {
        throw new ArgumentError('twitter collection username must be a valid Twitter/X handle', 'Example: opencli twitter collection @jack --until 2026-07-23T00:00:00Z');
    }
    if (!username) {
        await page.goto('https://x.com/home');
        await page.wait({ selector: '[data-testid="primaryColumn"]' });
        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 detect logged-in user. Are you logged in?');

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Pass a bare handle, with or without a leading @: `opencli twitter tweets @jack --limit 20`.
  2. Strip the URL down to the screen name if you copied a profile link (x.com/jack -> jack).
  3. Quote the argument in your shell if it contains special characters.
  4. Check for invisible characters/whitespace pasted from a document; retype the handle.

Example fix

// before
opencli twitter tweets https://x.com/jack --limit 20
// after
opencli twitter tweets @jack --limit 20
Defensive patterns

Strategy: validation

Validate before calling

const SCREEN_NAME_RE = /^[A-Za-z0-9_]{1,15}$/;
function toHandle(raw) {
  const s = String(raw ?? '').trim().replace(/^@/, '');
  if (!SCREEN_NAME_RE.test(s)) {
    throw new Error(`'${raw}' is not a valid Twitter/X handle; pass a bare name like @jack`);
  }
  return s;
}
// use: opencli twitter tweets ${toHandle(input)} --limit 20

Type guard

function isValidHandle(raw) {
  return /^[A-Za-z0-9_]{1,15}$/.test(String(raw ?? '').trim().replace(/^@/, ''));
}

Try / catch

try {
  await cli.run(['twitter', 'tweets', username, '--limit', '20']);
} catch (e) {
  if (/must be a valid Twitter\/X handle/.test(e.message)) {
    console.error('Extract the screen name from URLs first, e.g. x.com/jack -> jack');
  } else throw e;
}

Prevention

When it happens

Trigger: 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'.

Common situations: 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.

Related errors


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