{"record":{"id":"6646aae34068efc6","repo":"jackwener/OpenCLI","slug":"use-either-username-or-tweet-url-not-both","errorCode":null,"errorMessage":"Use either <username> or --tweet-url, not both","messagePattern":"Use either <username> or --tweet-url, not both","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/twitter/download.js","lineNumber":326,"sourceCode":"    strategy: Strategy.COOKIE,\n    browser: true,\n    args: [\n        { name: 'username', positional: true, help: 'Twitter username (with or without @) to scan their profile media. Either <username> or --tweet-url is required.' },\n        { name: 'tweet-url', help: 'Single tweet URL to download. Use this OR <username>, not both required at once.' },\n        { 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.' },\n        { name: 'output', default: './twitter-downloads', help: 'Output directory (default ./twitter-downloads). A per-source subdir is created inside.' },\n    ],\n    columns: ['index', 'tweet_id', 'url', 'type', 'status', 'size'],\n    func: async (page, kwargs) => {\n        try {\n            const rawUsername = String(kwargs.username ?? '').trim();\n            const tweetUrl = String(kwargs['tweet-url'] ?? '').trim();\n            const output = kwargs.output;\n            if (!rawUsername && !tweetUrl) {\n                throw new ArgumentError('twitter download requires either <username> or --tweet-url');\n            }\n            if (rawUsername && tweetUrl) {\n                throw new ArgumentError('Use either <username> or --tweet-url, not both');\n            }\n            if (tweetUrl) {\n                return downloadSingleTweet(page, tweetUrl, output);\n            }\n            const limit = requireLimit(kwargs.limit);\n            const username = normalizeTwitterScreenName(rawUsername);\n            if (!username) {\n                throw new ArgumentError('twitter download username must be a valid Twitter/X handle', 'Example: opencli twitter download @jack --limit 20');\n            }\n            return downloadUserMedia(page, username, limit, output);\n        }\n        catch (err) {\n            if (err instanceof CliError) throw err;\n            throw new CommandExecutionError(`twitter download failed: ${err?.message ?? String(err)}`);\n        }\n    },\n});\n","sourceCodeStart":308,"sourceCodeEnd":344,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/download.js#L308-L344","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Remove the positional <username> and keep only --tweet-url when downloading a single tweet","Remove --tweet-url and keep only the username when scraping a profile's media","Fix wrapper scripts/aliases to pass only one of the two values conditionally"],"exampleFix":"// before\nopencli twitter download @jack --tweet-url https://x.com/jack/status/123\n// after\nopencli twitter download --tweet-url https://x.com/jack/status/123","handlingStrategy":"validation","validationCode":"if (username && tweetUrl) {\n  throw new Error('Pass either <username> or --tweet-url, not both');\n}\nif (!username && !tweetUrl) {\n  throw new Error('Pass <username> or --tweet-url');\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["In scripts, build the argument list conditionally so only one targeting mode is ever appended","Quote shell variables carefully to avoid accidentally passing empty-looking extra args"],"tags":["cli","argument-validation","twitter"],"backgroundTag":"mutually-exclusive-arguments","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}