jackwener/OpenCLI · error · Error

Image upload failed: ${msg}

Error message

Image upload failed: ${msg}

What it means

Thrown by attachComposerImage when the CDP-based page.setFileInput bridge exists and is invoked, but fails with an error that is NOT a recoverable 'unknown action / not supported / not-allowed' condition (see isRecoverableFileInputError). Only non-recoverable failures get wrapped as `Image upload failed: ${msg}`; recoverable ones silently fall through to the base64 DataTransfer fallback.

Source

Thrown at clis/twitter/utils.js:139

 * After upload it polls the DOM briefly to confirm the preview thumbnail
 * actually rendered — without this, a 200 from setFileInput could mask a
 * silent-no-attachment post.
 *
 * @param {object} page - OpenCLI page handle.
 * @param {string} absImagePath - Already-validated absolute path.
 * @param {string} [fileInputSelector] - Override (post.js historically used
 *   the same selector; default matches the X composer route).
 */
export async function attachComposerImage(page, absImagePath, fileInputSelector = COMPOSER_FILE_INPUT_SELECTOR) {
    let uploaded = false;
    if (page.setFileInput) {
        try {
            await page.setFileInput([absImagePath], fileInputSelector);
            uploaded = true;
        } catch (err) {
            const msg = err instanceof Error ? err.message : String(err);
            if (!isRecoverableFileInputError(msg)) {
                throw new Error(`Image upload failed: ${msg}`);
            }
            // setFileInput not supported by extension — fall through to base64 fallback.
        }
    }
    if (!uploaded) {
        const ext = path.extname(absImagePath).toLowerCase();
        const mimeType = ext === '.png'
            ? 'image/png'
            : ext === '.gif'
                ? 'image/gif'
                : ext === '.webp'
                    ? 'image/webp'
                    : 'image/jpeg';
        const base64 = fs.readFileSync(absImagePath).toString('base64');
        if (base64.length > 500_000) {
            console.warn(`[warn] Image base64 payload is ${(base64.length / 1024 / 1024).toFixed(1)}MB. ` +
                'This may fail with the browser bridge. Update the extension to v1.6+ for CDP-based upload, ' +
                'or compress the image before attaching.');

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run the command after reloading the composer page so the file input exists again
  2. Check that the file input selector (default COMPOSER_FILE_INPUT_SELECTOR or your override) matches the current X compose DOM
  3. Update the OpenCLI browser extension to the latest version so setFileInput behaves as expected
  4. If the wrapped message indicates a browser/CDP failure, restart the browser session and retry; as a last resort compress the image so the base64 fallback path is viable
Defensive patterns

Strategy: try-catch

Validate before calling

if (!fs.existsSync(absImagePath) || !path.isAbsolute(absImagePath)) throw new Error('Provide an absolute path to an existing image before calling attachComposerImage');

Type guard

function isRecoverableUploadError(err) {
  const msg = err instanceof Error ? err.message : String(err);
  return /unknown action|not supported|not[-\s]?allowed|notallowederror/i.test(msg);
}

Try / catch

try {
  await attachComposerImage(page, absPath);
} catch (err) {
  if (err.message.startsWith('Image upload failed:') && !isRecoverableFileInputError(err.message)) {
    console.error('CDP setFileInput failed:', err.message);
    // reload the composer page or update the extension, then retry
  } else { throw err; }
}

Prevention

When it happens

Trigger: page.setFileInput([absImagePath], selector) throws for reasons outside the recoverable regex: the composer file input selector matches nothing in the CDP bridge, the page/extension bridge errors mid-injection, a protocol/timeout error from the browser, or a permissions failure not phrased as NotAllowedError.

Common situations: Stale DOM after X re-renders the composer so the selector no longer resolves; outdated or missing OpenCLI browser extension producing unexpected bridge errors; browser crashed or tab navigated away mid-upload; selector override argument pointing at the wrong page's input.

Related errors


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