jackwener/OpenCLI · error · CommandExecutionError

WeChat image upload failed: ${message}

Error message

WeChat image upload failed: ${message}

What it means

Thrown by injectImageFile when the primary upload path — setting the file input directly via page.setFileInput with the image's absolute path — fails with an error the library does not classify as recoverable. The original error message is wrapped into this CommandExecutionError.

Source

Thrown at clis/weixin/create-draft.js:121

        return Array.from(editor.querySelectorAll('img')).map(function(img) {
            return img.getAttribute('data-src') || img.getAttribute('src') || '';
        }).filter(function(src) { return /(?:mmbiz|qpic\\.cn)/i.test(src); });
    })()`);
    if (!Array.isArray(keys)) {
        throw new CommandExecutionError('WeChat image upload returned a malformed editor image payload.');
    }
    return keys.map(String);
}

async function injectImageFile(page, image) {
    if (typeof page.setFileInput === 'function') {
        try {
            await page.setFileInput([image.absPath], IMAGE_FILE_INPUT_SELECTOR);
            return;
        } catch (error) {
            if (!isRecoverableFileInputError(error)) {
                const message = error instanceof Error ? error.message : String(error);
                throw new CommandExecutionError(`WeChat image upload failed: ${message}`);
            }
        }
    }

    const base64 = fs.readFileSync(image.absPath).toString('base64');
    const fallback = await evaluate(page, `(() => {
        var input = document.querySelector(${JSON.stringify(IMAGE_FILE_INPUT_SELECTOR)});
        if (!input) return { ok: false, reason: 'image file input not found' };
        try {
            var binary = atob(${JSON.stringify(base64)});
            var bytes = new Uint8Array(binary.length);
            for (var i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
            var transfer = new DataTransfer();
            transfer.items.add(new File([bytes], ${JSON.stringify(image.fileName)}, { type: ${JSON.stringify(image.mimeType)} }));
            var descriptor = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'files');
            if (descriptor && descriptor.set) descriptor.set.call(input, transfer.files);
            else input.files = transfer.files;
            if (!input.files || input.files.length !== 1) return { ok: false, reason: 'file input rejected fallback file' };

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Verify image.absPath exists and is a readable image file before calling the command.
  2. Ensure the upload dialog/file input is present before setFileInput runs (add a wait or poll for the input).
  3. Update or fix isRecoverableFileInputError if the underlying error should fall through to the base64 fallback path instead of throwing.
  4. Check the driver supports page.setFileInput with your image type; upgrade the automation driver if needed.

Example fix

// before
catch (error) {
    if (!isRecoverableFileInputError(error)) {
        const message = error instanceof Error ? error.message : String(error);
        throw new CommandExecutionError(`WeChat image upload failed: ${message}`);
    }
}
// after
catch (error) {
    if (!isRecoverableFileInputError(error) && !isMissingFileInputError(error)) {
        const message = error instanceof Error ? error.message : String(error);
        throw new CommandExecutionError(`WeChat image upload failed: ${message}`);
    }
    // otherwise fall through to base64 evaluate() fallback
}
Defensive patterns

Strategy: fallback

Validate before calling

const fs = require('fs');
if (!fs.existsSync(image.absPath)) throw new Error(`image missing: ${image.absPath}`);
if (fs.statSync(image.absPath).size === 0) throw new Error(`image is empty: ${image.absPath}`);

Type guard

function hasSetFileInput(page) { return typeof page.setFileInput === 'function'; }

Try / catch

try {
    await createDraftCommand(opts);
} catch (e) {
    if (/WeChat image upload failed:/i.test(e.message)) {
        console.error('setFileInput path failed:', e.message);
        // ensure image path/format are valid, then retry
    }
    throw e;
}

Prevention

When it happens

Trigger: page.setFileInput throws because the file input selector (IMAGE_FILE_INPUT_SELECTOR) is absent, the input is hidden/detached, the driver lacks setFileInput support (but isRecoverableFileInputError deems the failure non-recoverable), or the image path is invalid and the driver rejects the file.

Common situations: WeChat changed the upload dialog's file input markup; the automation driver version lacks or changed setFileInput semantics; the image absPath points to a missing/unreadable file; the dialog did not open before the call.

Related errors


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