{"record":{"id":"3dca638ddb015a66","repo":"jackwener/OpenCLI","slug":"result-reason-unknown","errorCode":null,"errorMessage":"点击草稿按钮失败: ${result?.reason || 'unknown'}","messagePattern":"点击草稿按钮失败: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/douyin/draft.js","lineNumber":262,"sourceCode":"    if (props?.onClick) {\n      props.onClick({\n        preventDefault() {},\n        stopPropagation() {},\n        nativeEvent: null,\n        target: btn,\n        currentTarget: btn,\n      });\n    } else {\n      btn.click();\n    }\n    return {\n      ok: true,\n      text: (btn.textContent || '').trim(),\n      creationId,\n    };\n  }`));\n    if (!result?.ok) {\n        throw new CommandExecutionError(`点击草稿按钮失败: ${result?.reason || 'unknown'}`);\n    }\n    if (!result.creationId) {\n        throw new CommandExecutionError('点击草稿按钮失败: creation-id-missing');\n    }\n    return {\n        text: result.text || '暂存离开',\n        creationId: result.creationId,\n    };\n}\n/**\n * Wait until creator center shows the resumable-draft prompt after saving.\n */\nasync function waitForDraftResult(page, creationId) {\n    let lastState = { href: '', bodyText: '' };\n    for (let attempt = 0; attempt < 20; attempt += 1) {\n        lastState = (await page.evaluate(`() => ({\n      href: location.href,\n      bodyText: document.body?.innerText || ''","sourceCodeStart":244,"sourceCodeEnd":280,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/douyin/draft.js#L244-L280","documentation":"clickSaveDraft locates the '暂存离开' button, extracts the creation_id from the React fiber tree, and invokes the button's onClick. If the in-page script reports ok:false it throws this CommandExecutionError embedding the reason (commonly 'draft-button-missing').","triggerScenarios":"The '暂存离开' button is absent or renamed at save time — a modal covers it, the composer layout changed, or the button renders as a non-<button> element (so the HTMLButtonElement instanceof check fails).","commonSituations":"Douyin changed the save button copy or element type; a confirmation dialog or coach mark appeared right before saving; page re-rendered between form fill and save removing the button.","solutions":["Retry once — transient modals/re-renders are common and dismissKnownModals runs on the next attempt","Inspect the page and update the '暂存离开' text / HTMLButtonElement selector in clis/douyin/draft.js:235-240 if Douyin changed the button","Broaden the query to '[role=\"button\"]' or span elements styled as buttons"],"exampleFix":"// before\nif (!(btn instanceof HTMLButtonElement)) {\n  return { ok: false, reason: 'draft-button-missing' };\n}\n// after: accept role=button too\nif (!(btn instanceof HTMLElement)) {\n  return { ok: false, reason: 'draft-button-missing' };\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await opencli.douyin.draft({ video, title });\n} catch (e) {\n  if (e.message.includes('点击草稿按钮失败') && !e.message.includes('creation-id')) {\n    // draft button missing — retry; ensure no modal intercepted the click\n  }\n}","preventionTips":["Run dismissKnownModals immediately before saving","Broaden the save-button selector beyond strict HTMLButtonElement","Monitor Douyin UI changes to the '暂存离开' button"],"tags":["douyin","dom-selector","save-draft","browser-automation"],"backgroundTag":"selector-not-found","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}