{"record":{"id":"20966c3bf35af34f","repo":"jackwener/OpenCLI","slug":"wechat-image-upload-timed-out-before-a-new-cdn-ima","errorCode":null,"errorMessage":"WeChat image upload timed out before a new CDN image appeared in the editor.","messagePattern":"WeChat image upload timed out before a new CDN image appeared in the editor\\.","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/weixin/create-draft.js","lineNumber":193,"sourceCode":"        const state = await evaluate(page, `(() => {\n            var previous = new Set(${JSON.stringify(previousKeys)});\n            var editor = document.querySelector('#ueditor_0');\n            var images = editor ? Array.from(editor.querySelectorAll('img')) : [];\n            var key = images.map(function(img) {\n                return img.getAttribute('data-src') || img.getAttribute('src') || '';\n            }).find(function(src) { return /(?:mmbiz|qpic\\\\.cn)/i.test(src) && !previous.has(src); });\n            var errorText = Array.from(document.querySelectorAll('.weui-desktop-tips, .weui-desktop-toast, .js_msgSenderTips'))\n                .filter(function(el) { return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length); })\n                .map(function(el) { return (el.innerText || el.textContent || '').trim(); })\n                .filter(Boolean).join('\\\\n');\n            return { ok: !!key, errorText: errorText };\n        })()`);\n        if (state?.ok) return;\n        if (state?.errorText && /(无法解析|上传失败|过大|频繁|不支持|错误)/.test(state.errorText)) {\n            throw new CommandExecutionError(`WeChat image upload failed: ${state.errorText}`);\n        }\n    }\n    throw new CommandExecutionError('WeChat image upload timed out before a new CDN image appeared in the editor.');\n}\n\nasync function selectCoverFromContent(page) {\n    await evaluate(page, 'document.querySelector(\"#js_cover_description_area\")?.scrollIntoView()');\n    await page.wait(1);\n    await evaluate(page, 'document.querySelector(\".js_cover_btn_area\")?.click()');\n    await page.wait(1);\n    await evaluate(page, `(() => {\n        var link = Array.from(document.querySelectorAll('a.pop-opr__button')).find(function(el) {\n            return (el.textContent || '').trim() === '从正文选择';\n        });\n        if (link) link.click();\n    })()`);\n    await page.wait(2);\n    await evaluate(page, `document.querySelector('.weui-desktop-dialog_img-picker .appmsg_content_img')?.click()`);\n    await page.wait(1);\n    await evaluate(page, `(() => {\n        var button = Array.from(document.querySelectorAll('.weui-desktop-dialog_img-picker button')).find(function(el) {","sourceCodeStart":175,"sourceCodeEnd":211,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/weixin/create-draft.js#L175-L211","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Retry with a smaller/compressed image to reduce upload time below the 30-second polling budget.","Check the network connection and WeChat service status; transient slowness can exceed the polling window.","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.","Increase the poll attempt count or interval for large images or slow environments."],"exampleFix":"// before\nfor (let attempt = 0; attempt < 15; attempt++) {\n    await page.wait(2);\n    ...\n}\n// after\nconst maxAttempts = process.env.WEIXIN_UPLOAD_POLL_ATTEMPTS ? Number(process.env.WEIXIN_UPLOAD_POLL_ATTEMPTS) : 15;\nfor (let attempt = 0; attempt < maxAttempts; attempt++) {\n    await page.wait(2);\n    ...\n}","handlingStrategy":"retry","validationCode":"const fs = require('fs');\nif (fs.statSync(image.absPath).size > 5 * 1024 * 1024) throw new Error('image too large; upload may exceed the 30s detection window — compress first');","typeGuard":"function isPollState(v) { return typeof v === 'object' && v !== null && ('ok' in v); }","tryCatchPattern":"try {\n    await createDraftCommand(opts);\n} catch (e) {\n    if (/timed out before a new CDN image/i.test(e.message)) {\n        await sleep(10000);\n        return createDraftCommand(opts); // retry once with a pause\n    }\n    throw e;\n}","preventionTips":["Keep images small so uploads finish within the ~30s polling budget.","Ensure a stable network connection; avoid running on flaky CI networks.","Retry once after a pause before investigating deeper.","Check the CDN-key detection selectors still match WeChat's inserted <img> markup."],"tags":["wechat","timeout","image","upload"],"backgroundTag":"upload-timeout","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}