jackwener/OpenCLI · warning · ArgumentError

twitter download requires either <username> or --tweet-url

Error message

twitter download requires either <username> or --tweet-url

What it means

The twitter download CLI requires exactly one input selector: a positional <username> to scan a profile's media, or --tweet-url for a single tweet. This ArgumentError is thrown before any network work when neither argument is provided (and a sibling error fires when both are). It is an ordinary usage/validation error, not a runtime failure.

Source

Thrown at clis/twitter/download.js:323

    access: 'read',
    description: 'Download Twitter/X media (images and videos). Provide either <username> to fetch every media item from their profile via the GraphQL UserMedia endpoint with cursor pagination, or --tweet-url to download a single tweet.',
    domain: 'x.com',
    strategy: Strategy.COOKIE,
    browser: true,
    args: [
        { name: 'username', positional: true, help: 'Twitter username (with or without @) to scan their profile media. Either <username> or --tweet-url is required.' },
        { name: 'tweet-url', help: 'Single tweet URL to download. Use this OR <username>, not both required at once.' },
        { name: 'limit', type: 'int', default: 10, help: 'Maximum number of media items to download when scanning a profile (default 10). Ignored when --tweet-url is used.' },
        { name: 'output', default: './twitter-downloads', help: 'Output directory (default ./twitter-downloads). A per-source subdir is created inside.' },
    ],
    columns: ['index', 'tweet_id', 'url', 'type', 'status', 'size'],
    func: async (page, kwargs) => {
        try {
            const rawUsername = String(kwargs.username ?? '').trim();
            const tweetUrl = String(kwargs['tweet-url'] ?? '').trim();
            const output = kwargs.output;
            if (!rawUsername && !tweetUrl) {
                throw new ArgumentError('twitter download requires either <username> or --tweet-url');
            }
            if (rawUsername && tweetUrl) {
                throw new ArgumentError('Use either <username> or --tweet-url, not both');
            }
            if (tweetUrl) {
                return downloadSingleTweet(page, tweetUrl, output);
            }
            const limit = requireLimit(kwargs.limit);
            const username = normalizeTwitterScreenName(rawUsername);
            if (!username) {
                throw new ArgumentError('twitter download username must be a valid Twitter/X handle', 'Example: opencli twitter download @jack --limit 20');
            }
            return downloadUserMedia(page, username, limit, output);
        }
        catch (err) {
            if (err instanceof CliError) throw err;
            throw new CommandExecutionError(`twitter download failed: ${err?.message ?? String(err)}`);
        }

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Pass a username positionally: opencli twitter download @handle --limit 20
  2. Or pass a single tweet: opencli twitter download --tweet-url https://x.com/user/status/1234567890
  3. Provide exactly one of the two — not both, or the sibling 'Use either <username> or --tweet-url, not both' error fires.
  4. Check your shell/CI quoting so the positional username is actually passed through to the CLI.

Example fix

// before
await opencli twitter download --limit 10
// ArgumentError: twitter download requires either <username> or --tweet-url

// after
await opencli twitter download @jack --limit 10
Defensive patterns

Strategy: validation

Validate before calling

const rawUsername = String(kwargs.username ?? '').trim();
const tweetUrl = String(kwargs['tweet-url'] ?? '').trim();
if (!rawUsername && !tweetUrl) {
    throw new ArgumentError('twitter download requires either <username> or --tweet-url');
}
if (rawUsername && tweetUrl) {
    throw new ArgumentError('Use either <username> or --tweet-url, not both');
}

Type guard

function hasValidInput(args) {
    const u = String(args?.username ?? '').trim();
    const t = String(args?.['tweet-url'] ?? '').trim();
    return Boolean(u) !== Boolean(t); // exactly one present
}

Try / catch

try {
    await twitterDownload(...args);
} catch (err) {
    if (err instanceof ArgumentError && err.message.includes('requires either')) {
        console.error('Usage: opencli twitter download <username> | --tweet-url <url>');
        process.exitCode = 2;
    } else throw err;
}

Prevention

When it happens

Trigger: Calling 'twitter download' with no positional username and no --tweet-url flag (e.g. passing only --limit or --output).

Common situations: Scripting the CLI and forgetting the positional argument; quoting/argument-passing bugs where the username is swallowed by the shell; mistaking --tweet-url's flag name (using --url or a positional tweet URL instead); CI configs missing required parameters.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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