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

The reply command accepts an image either as a local path (--image) or as a remote URL (--image-url), but not both at once. Supplying both is ambiguous (which one gets attached?), so the command fails fast with this CommandExecutionError before any navigation or posting.

Source

Thrown at clis/twitter/reply.js:265

    site: 'twitter',
    name: 'reply',
    access: 'write',
    description: 'Reply to a specific tweet, optionally with a local or remote image',
    domain: 'x.com',
    strategy: Strategy.UI, // Uses the UI directly to input and click post
    browser: true,
    args: [
        { name: 'url', type: 'string', required: true, positional: true, help: 'The URL of the tweet to reply to' },
        { name: 'text', type: 'string', required: true, positional: true, help: 'The text content of your reply' },
        { name: 'image', help: 'Optional local image path to attach to the reply' },
        { name: 'image-url', help: 'Optional remote image URL to download and attach to the reply' },
    ],
    columns: ['status', 'message', 'text', 'url'],
    func: async (page, kwargs) => {
        if (!page)
            throw new CommandExecutionError('Browser session required for twitter reply');
        if (kwargs.image && kwargs['image-url']) {
            throw new CommandExecutionError('Use either --image or --image-url, not both.');
        }
        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 normally more reliable than the inline
            // tweet page reply box, but X occasionally leaves that route on the
            // Home timeline behind a loading dialog. openReplyComposer falls
            // back to the target tweet's visible Reply action.
            const composer = await openReplyComposer(page, kwargs.url);
            if (!composer?.ok) {
                throw new CommandExecutionError(composer?.message ?? 'Could not open the reply composer.', 'Open the tweet in the browser and check whether the reply box is available.');

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Remove one of the two flags — keep --image for local files or --image-url for remote URLs.
  2. If the remote image is wanted, download it first and pass only --image, or vice versa.
  3. Check wrapper scripts/config for a default --image-url that collides with a manual --image.

Example fix

// before
opencli twitter reply --url <tweet> --text "hi" --image ./pic.png --image-url https://x.com/pic.png
// after
opencli twitter reply --url <tweet> --text "hi" --image ./pic.png
Defensive patterns

Strategy: validation

Validate before calling

if (opts.image && opts.imageUrl) {
  throw new Error('Pass either --image or --image-url, not both');
}

Type guard

null

Try / catch

try {
  await cli('twitter', 'reply', { url, text, image });
} catch (e) {
  if (String(e.message).includes('Use either --image or --image-url')) {
    // drop the conflicting flag and retry with one image source
  } else throw e;
}

Prevention

When it happens

Trigger: Calling `twitter reply --image ./pic.png --image-url https://example.com/pic.png` — both truthy kwargs trigger the check at clis/twitter/reply.js:265.

Common situations: Script templates where both flags were filled in by defaults; copy-pasting an example command and leaving both image flags; a config file injecting --image-url on top of a manually passed --image.

Related errors


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