jackwener/OpenCLI · error · CommandExecutionError

Use either --image or --image-url, not both.

Error message

Use either --image or --image-url, not both.

What it means

CommandExecutionError thrown when both --image (local file path) and --image-url (remote URL) are supplied to the twitter quote command. The two options are mutually exclusive ways to attach one image, so passing both is treated as an input contradiction and rejected before any browser interaction.

Source

Thrown at clis/twitter/quote.js:127

    site: 'twitter',
    name: 'quote',
    access: 'write',
    description: 'Quote-tweet a specific tweet with your own text, optionally with a local or remote image',
    domain: 'x.com',
    strategy: Strategy.UI,
    browser: true,
    args: [
        { name: 'url', type: 'string', required: true, positional: true, help: 'The URL of the tweet to quote' },
        { name: 'text', type: 'string', required: true, positional: true, help: 'The text content of your quote' },
        { name: 'image', help: 'Optional local image path to attach to the quote tweet' },
        { name: 'image-url', help: 'Optional remote image URL to download and attach to the quote tweet' },
    ],
    columns: ['status', 'message', 'text'],
    func: async (page, kwargs) => {
        if (!page)
            throw new CommandExecutionError('Browser session required for twitter quote');
        if (kwargs.image && kwargs['image-url']) {
            throw new CommandExecutionError('Use either --image or --image-url, not both.');
        }

        // Validate URL (typed ArgumentError on malformed/off-domain inputs)
        // before any browser interaction or remote image download.
        const target = parseTweetUrl(kwargs.url);

        let localImagePath;
        let cleanupDir;
        try {
            if (kwargs.image) {
                localImagePath = resolveImagePath(kwargs.image);
            } else if (kwargs['image-url']) {
                const downloaded = await downloadRemoteImage(kwargs['image-url']);
                localImagePath = downloaded.absPath;
                cleanupDir = downloaded.cleanupDir;
            }

            // Dedicated composer is more reliable than the inline quote-tweet button.

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Pass only one of --image or --image-url
  2. In automation, resolve the attachment source first and set exactly one flag
  3. Catch this error and surface the mutual-exclusion message to the user before retry
  4. Add input validation in your wrapper to drop image-url when image is present (or vice versa)

Example fix

// before
await cli.run('twitter quote', { url, text, image: './pic.png', 'image-url': 'https://x/pic.png' });
// after
await cli.run('twitter quote', { url, text, 'image-url': 'https://x/pic.png' });
Defensive patterns

Strategy: validation

Validate before calling

if (kwargs.image && kwargs['image-url']) {
  throw new Error('Pass only one of --image or --image-url');
}

Try / catch

try {
  await cli.run('twitter quote', kwargs);
} catch (e) {
  if (e instanceof CommandExecutionError && /--image or --image-url/.test(e.message)) {
    const fixed = { ...kwargs, 'image-url': undefined }; // keep local image only
    return cli.run('twitter quote', fixed);
  }
  throw e;
}

Prevention

When it happens

Trigger: Invoking twitter quote with both kwargs.image and kwargs['image-url'] set, e.g. a config/template that fills both attachment options.

Common situations: Automated pipelines merging default flag values with user-provided ones; copy-pasting an example that sets both; a wrapper adding image-url when the caller already passed image.

Related errors


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