{"record":{"id":"d90b6b223aaf2f81","repo":"jackwener/OpenCLI","slug":"wechat-uploaded-the-image-but-did-not-confirm-it-a","errorCode":null,"errorMessage":"WeChat uploaded the image but did not confirm it as the draft cover.","messagePattern":"WeChat uploaded the image but did not confirm it as the draft cover\\.","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/weixin/create-draft.js","lineNumber":310,"sourceCode":"    func: async (page, kwargs) => {\n        try {\n            const coverImage = kwargs['cover-image'] ? resolveCoverImage(kwargs['cover-image']) : null;\n            await navigateToEditor(page);\n\n            const titleResult = await fillField(page, 'textarea#title', kwargs.title);\n            if (!titleResult?.ok) throw new CommandExecutionError('Failed to fill title');\n            if (kwargs.author) {\n                const authorResult = await fillField(page, 'input#author', kwargs.author);\n                if (!authorResult?.ok) throw new CommandExecutionError('Failed to fill author');\n            }\n            const contentResult = await fillContent(page, kwargs.content);\n            if (!contentResult?.ok) throw new CommandExecutionError('Failed to fill content');\n\n            if (coverImage) {\n                await uploadContentImage(page, coverImage);\n                const coverSet = await selectCoverFromContent(page);\n                if (coverSet !== true) {\n                    throw new CommandExecutionError('WeChat uploaded the image but did not confirm it as the draft cover.');\n                }\n            }\n\n            if (kwargs.summary) {\n                const summaryResult = await fillField(page, 'textarea#js_description', kwargs.summary);\n                if (!summaryResult?.ok) throw new CommandExecutionError('Failed to fill summary');\n            }\n\n            await saveDraft(page);\n            return [{\n                status: 'draft saved',\n                detail: `\"${kwargs.title}\"${kwargs.author ? ` by ${kwargs.author}` : ''}${coverImage ? ' (with cover)' : ''}`,\n            }];\n        } catch (error) {\n            if (error instanceof CliError) throw error;\n            const message = error instanceof Error ? error.message : String(error);\n            throw new CommandExecutionError(`WeChat create-draft failed: ${message}`);\n        }","sourceCodeStart":292,"sourceCodeEnd":328,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/weixin/create-draft.js#L292-L328","documentation":"This CommandExecutionError is thrown when selectCoverFromContent() does not return exactly true after the cover image was uploaded into the article body. The flow uploads the image via uploadContentImage(), then asks WeChat to set it as the draft cover; any non-true result (timeout, dialog not found, WeChat UI change) produces this error. Unlike the fill errors, the upload itself succeeded — only the cover-selection confirmation failed.","triggerScenarios":"Passing --cover-image where uploadContentImage succeeds but selectCoverFromContent returns false/undefined: the cover-selection dialog did not open or confirm within its timeouts, WeChat changed the 'set as cover' UI flow, the uploaded image did not appear in the editor (CDN key scan found nothing), or WeChat silently rejected the image format/dimensions.","commonSituations":"WeChat A/B tests a new cover-picker dialog so selectCoverFromContent's selectors no longer match; image fails WeChat's cover aspect-ratio/size requirements so the confirm step is skipped; slow CDN upload races the selection step; GIF/WebP covers rejected by a server-side rule despite local MIME validation passing.","solutions":["Re-run with a standard JPEG/PNG cover sized to WeChat's recommended cover ratio (~2.35:1, e.g., 900x383) — dimension/format rejection is a frequent silent cause.","Retry once — slow image upload racing the cover-selection step often resolves on a second attempt.","Inspect selectCoverFromContent() in clis/weixin/create-draft.js against the live editor UI; update its dialog selectors if WeChat changed the cover flow.","Drop --cover-image and set the cover manually in the WeChat web UI to unblock draft creation, then fix the automation."],"exampleFix":"// before\nconst coverSet = await selectCoverFromContent(page);\nif (coverSet !== true) {\n    throw new CommandExecutionError('WeChat uploaded the image but did not confirm it as the draft cover.');\n}\n// after\nconst coverSet = await selectCoverFromContent(page);\nif (coverSet !== true) {\n    throw new CommandExecutionError(`Cover upload succeeded but cover selection returned ${JSON.stringify(coverSet)}; check selectCoverFromContent selectors and image dimensions (WeChat requires ~2.35:1).`);\n}","handlingStrategy":"try-catch","validationCode":"const stat = fs.statSync(coverPath);\nconst isImage = /\\.(jpe?g|png|gif|webp)$/i.test(coverPath);\n// additionally keep dimensions near WeChat's ~2.35:1 cover ratio\nif (!stat.isFile() || !isImage) throw new Error('Cover must be an existing JPEG/PNG/GIF/WebP file, ideally ~2.35:1 (e.g. 900x383).');","typeGuard":"function isUsableCover(p) {\n  return typeof p === 'string' && p.length > 0 && /\\.(jpe?g|png|gif|webp)$/i.test(p);\n}","tryCatchPattern":"try {\n  await createDraft({ title, content, coverImage });\n} catch (e) {\n  if (e instanceof CommandExecutionError && /did not confirm it as the draft cover/.test(e.message)) {\n    // fallback: create the draft without the cover, or retry with a differently sized JPEG\n  } else throw e;\n}","preventionTips":["Use JPEG/PNG covers close to WeChat's recommended 2.35:1 ratio (e.g., 900x383); avoid exotic sizes and GIF/WebP for covers.","Prefer a stable network; slow CDN uploads race the cover-selection confirmation step.","If cover selection is not critical, create the draft without --cover-image and attach the cover in the WeChat UI.","After WeChat UI updates, verify the cover-picker flow used by selectCoverFromContent still matches."],"tags":["puppeteer","wechat","image-upload","ui-automation"],"backgroundTag":"ui-automation-step-not-confirmed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}