{"record":{"id":"cfd7a3690069e6f2","repo":"jackwener/OpenCLI","slug":"wechat-draft-save-was-not-confirmed-by-a-fresh-suc","errorCode":null,"errorMessage":"WeChat draft save was not confirmed by a fresh success status.","messagePattern":"WeChat draft save was not confirmed by a fresh success status\\.","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/weixin/create-draft.js","lineNumber":270,"sourceCode":"\nasync function saveDraft(page) {\n    const before = await readSaveState(page);\n    const result = await evaluate(page, `(() => {\n        var button = Array.from(document.querySelectorAll('span, button, a')).find(function(el) {\n            return (el.textContent || '').trim() === '保存为草稿' && !el.disabled;\n        });\n        if (!button) return { ok: false };\n        button.click();\n        return { ok: true };\n    })()`);\n    if (!result?.ok) throw new CommandExecutionError('WeChat save-draft button was not found.');\n\n    for (let attempt = 0; attempt < 8; attempt++) {\n        await page.wait(1);\n        const state = await readSaveState(page);\n        if (state?.visible && (!before?.visible || state.text !== before.text)) return;\n    }\n    throw new CommandExecutionError('WeChat draft save was not confirmed by a fresh success status.');\n}\n\nexport const createDraftCommand = cli({\n    site: 'weixin',\n    name: 'create-draft',\n    access: 'write',\n    description: '创建微信公众号图文草稿',\n    domain: WEIXIN_DOMAIN,\n    strategy: Strategy.COOKIE,\n    browser: true,\n    navigateBefore: false,\n    args: [\n        { name: 'title', required: true, help: '文章标题 (最长64字)' },\n        { name: 'content', required: true, positional: true, help: '文章正文' },\n        { name: 'author', help: '作者名 (最长8字)' },\n        { name: 'cover-image', help: '封面图片路径 (会先上传到正文再设为封面)' },\n        { name: 'summary', help: '文章摘要' },\n        { name: 'timeout', type: 'int', required: false, default: 180, help: 'Max seconds for the overall command (default: 180)' },","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/weixin/create-draft.js#L252-L288","documentation":"Thrown by saveDraft after the save-draft button was clicked but the 8-attempt confirmation loop (each with a 1-second wait) never observed a fresh save status via readSaveState — i.e. no visible status text appeared, or the text never changed from the pre-save snapshot. The library requires positive confirmation that the draft was saved.","triggerScenarios":"The click didn't register (overlay, animation), the save request failed server-side without changing the status text, the save finished but the status text was identical to the pre-save snapshot (before.text) so the freshness check never passed, or saving takes longer than the ~8-second polling window.","commonSituations":"Slow WeChat saves exceeding 8 seconds; editing an already-saved draft where the status text doesn't change; page modals (e.g. unsaved-changes prompts) swallowing the click; WeChat changing the status message element read by readSaveState.","solutions":["Retry the command; transient click or save failures often succeed on the second attempt.","Increase the polling attempts/duration to accommodate slow saves.","Check whether the status-text freshness comparison (state.text !== before.text) is too strict for repeat saves of an unchanged draft, and relax it or compare save timestamps.","Confirm the status element selector in readSaveState still matches WeChat's current UI and update it if the editor changed."],"exampleFix":"// before\nif (state?.visible && (!before?.visible || state.text !== before.text)) return;\n// after\nif (state?.visible && (!before?.visible || state.text !== before.text || state.text.includes('成功'))) return;","handlingStrategy":"retry","validationCode":"const before = await readSaveState(page); // capture pre-save status\n// ensure the save click happened; re-check button state\nconst clicked = await evaluate(page, 'Array.from(document.querySelectorAll(\"button, a\")).some(el => (el.textContent || \"\").trim() === \"保存为草稿\")');\nif (clicked !== true) throw new Error('save button gone; cannot confirm save');","typeGuard":"function isSaveState(v) { return typeof v === 'object' && v !== null && (typeof v.visible === 'boolean') && typeof v.text === 'string'; }","tryCatchPattern":"try {\n    await createDraftCommand(opts);\n} catch (e) {\n    if (/not confirmed by a fresh success status/i.test(e.message)) {\n        await sleep(5000);\n        return createDraftCommand(opts); // slow save — retry once\n    }\n    throw e;\n}","preventionTips":["Allow longer polling for slow networks before treating save as failed.","Verify no modal (e.g. confirm/unsaved-changes dialog) intercepts the save click.","Re-check readSaveState's status element selector after WeChat UI updates.","Verify the draft actually appeared in the MP draft list when this error fires repeatedly, to distinguish detection bugs from real save failures."],"tags":["wechat","timeout","confirmation","draft-save"],"backgroundTag":"save-not-confirmed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}