jackwener/OpenCLI · error · CommandExecutionError

WeChat image upload timed out before a new CDN image appeare

Error message

WeChat image upload timed out before a new CDN image appeared in the editor.

What it means

Thrown by uploadContentImage when the 15-attempt polling loop (each with a 2-second wait) completes without detecting a new mmbiz/qpic CDN image key in the editor and without any recognized WeChat error text. The upload neither succeeded nor produced a page-reported error within ~30 seconds.

Source

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

        const state = await evaluate(page, `(() => {
            var previous = new Set(${JSON.stringify(previousKeys)});
            var editor = document.querySelector('#ueditor_0');
            var images = editor ? Array.from(editor.querySelectorAll('img')) : [];
            var key = images.map(function(img) {
                return img.getAttribute('data-src') || img.getAttribute('src') || '';
            }).find(function(src) { return /(?:mmbiz|qpic\\.cn)/i.test(src) && !previous.has(src); });
            var errorText = Array.from(document.querySelectorAll('.weui-desktop-tips, .weui-desktop-toast, .js_msgSenderTips'))
                .filter(function(el) { return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length); })
                .map(function(el) { return (el.innerText || el.textContent || '').trim(); })
                .filter(Boolean).join('\\n');
            return { ok: !!key, errorText: errorText };
        })()`);
        if (state?.ok) return;
        if (state?.errorText && /(无法解析|上传失败|过大|频繁|不支持|错误)/.test(state.errorText)) {
            throw new CommandExecutionError(`WeChat image upload failed: ${state.errorText}`);
        }
    }
    throw new CommandExecutionError('WeChat image upload timed out before a new CDN image appeared in the editor.');
}

async function selectCoverFromContent(page) {
    await evaluate(page, 'document.querySelector("#js_cover_description_area")?.scrollIntoView()');
    await page.wait(1);
    await evaluate(page, 'document.querySelector(".js_cover_btn_area")?.click()');
    await page.wait(1);
    await evaluate(page, `(() => {
        var link = Array.from(document.querySelectorAll('a.pop-opr__button')).find(function(el) {
            return (el.textContent || '').trim() === '从正文选择';
        });
        if (link) link.click();
    })()`);
    await page.wait(2);
    await evaluate(page, `document.querySelector('.weui-desktop-dialog_img-picker .appmsg_content_img')?.click()`);
    await page.wait(1);
    await evaluate(page, `(() => {
        var button = Array.from(document.querySelectorAll('.weui-desktop-dialog_img-picker button')).find(function(el) {

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Retry with a smaller/compressed image to reduce upload time below the 30-second polling budget.
  2. Check the network connection and WeChat service status; transient slowness can exceed the polling window.
  3. Verify the success-detection script still matches how WeChat inserts uploaded images (img src/data-src with mmbiz/qpic) and update if the editor changed.
  4. Increase the poll attempt count or interval for large images or slow environments.

Example fix

// before
for (let attempt = 0; attempt < 15; attempt++) {
    await page.wait(2);
    ...
}
// after
const maxAttempts = process.env.WEIXIN_UPLOAD_POLL_ATTEMPTS ? Number(process.env.WEIXIN_UPLOAD_POLL_ATTEMPTS) : 15;
for (let attempt = 0; attempt < maxAttempts; attempt++) {
    await page.wait(2);
    ...
}
Defensive patterns

Strategy: retry

Validate before calling

const fs = require('fs');
if (fs.statSync(image.absPath).size > 5 * 1024 * 1024) throw new Error('image too large; upload may exceed the 30s detection window — compress first');

Type guard

function isPollState(v) { return typeof v === 'object' && v !== null && ('ok' in v); }

Try / catch

try {
    await createDraftCommand(opts);
} catch (e) {
    if (/timed out before a new CDN image/i.test(e.message)) {
        await sleep(10000);
        return createDraftCommand(opts); // retry once with a pause
    }
    throw e;
}

Prevention

When it happens

Trigger: The upload dialog silently closed, the file injection never actually started an upload, the upload is genuinely slow (large image, slow network) and exceeds the ~30s budget, or the success detection (new CDN key appearing in the editor) fails because WeChat changed how uploaded images are inserted into the DOM.

Common situations: Slow networks or large images exceeding the timeout; WeChat editor updates changing the img data-src/src attributes the detection script inspects; headless environments where the upload silently stalls.

Understand the failure class

Related errors


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