jackwener/OpenCLI · error · ArgumentError

Use either <username> or --tweet-url, not both

Error message

Use either <username> or --tweet-url, not both

What it means

opencli twitter download accepts exactly one targeting mode: a positional <username> for profile-wide media scraping, or --tweet-url for a single tweet. The command deliberately rejects supplying both to avoid ambiguous intent, throwing an ArgumentError (a CliError subclass) before any browser work starts.

Source

Thrown at clis/twitter/download.js:326

    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. Remove the positional <username> and keep only --tweet-url when downloading a single tweet
  2. Remove --tweet-url and keep only the username when scraping a profile's media
  3. Fix wrapper scripts/aliases to pass only one of the two values conditionally

Example fix

// before
opencli twitter download @jack --tweet-url https://x.com/jack/status/123
// after
opencli twitter download --tweet-url https://x.com/jack/status/123
Defensive patterns

Strategy: validation

Validate before calling

if (username && tweetUrl) {
  throw new Error('Pass either <username> or --tweet-url, not both');
}
if (!username && !tweetUrl) {
  throw new Error('Pass <username> or --tweet-url');
}

Prevention

When it happens

Trigger: Calling `opencli twitter download @jack --tweet-url https://x.com/user/status/123...` where both the positional username argument and the --tweet-url kwarg are non-empty after trimming.

Common situations: Shell aliases or wrapper scripts that always append a username; copy-pasting an example command that included both; scripting where a variable for tweet-url is set but a default username remains in the command line.

Related errors


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