{"record":{"id":"1b1646040651729c","repo":"jackwener/OpenCLI","slug":"lastreason","errorCode":null,"errorMessage":"准备抖音自定义封面输入框失败: ${lastReason}","messagePattern":"准备抖音自定义封面输入框失败: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/douyin/draft.js","lineNumber":158,"sourceCode":"        .slice(${JSON.stringify(baselineCount)})\n        .find((el) => el instanceof HTMLInputElement && !el.disabled);\n      if (!(target instanceof HTMLInputElement)) {\n        return { ok: false, reason: 'cover-input-pending' };\n      }\n\n      document\n        .querySelectorAll('[data-opencli-cover-input=\"1\"]')\n        .forEach((el) => el.removeAttribute('data-opencli-cover-input'));\n      target.setAttribute('data-opencli-cover-input', '1');\n      return { ok: true, selector: '[data-opencli-cover-input=\"1\"]' };\n    }`));\n        if (result?.ok && result.selector) {\n            return result.selector;\n        }\n        lastReason = result?.reason || lastReason;\n        await page.wait({ time: 0.5 });\n    }\n    throw new CommandExecutionError(`准备抖音自定义封面输入框失败: ${lastReason}`);\n}\n/**\n * Read the local quick-check panel text that reflects cover validation state.\n */\nexport function buildCoverCheckPanelTextJs() {\n    return `() => {\n    const normalize = (value) => (value || '').replace(/\\\\s+/g, ' ').trim();\n    const stateTexts = ['检测', '检测中', '封面检测中', '重新检测', '横/竖双封面缺失'];\n    const marker = Array.from(document.querySelectorAll('div,span,p,button')).find(\n      (el) => normalize(el.textContent) === '快速检测'\n    );\n    let root = marker?.parentElement || null;\n    while (root && root !== document.body) {\n      const descendants = Array.from(root.querySelectorAll('div,span,p,button'))\n        .map((el) => normalize(el.textContent));\n      const hasMarkerText = descendants.includes('快速检测');\n      const hasStateText = descendants.some((text) => stateTexts.includes(text));\n      if (hasMarkerText && hasStateText) {","sourceCodeStart":140,"sourceCodeEnd":176,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/douyin/draft.js#L140-L176","documentation":"prepareCustomCoverInput tries up to 20 times (0.5s apart) to click the '上传新封面' label and find a newly-appearing enabled file input, tagging it for CDP file injection. If it never finds one it throws this CommandExecutionError including the last internal reason, typically 'cover-input-missing' or 'cover-input-pending'.","triggerScenarios":"Passing --cover when the custom-cover panel never exposes a usable input[type=file]: the '上传新封面' label is absent (UI change), the input stays disabled, or the file-input count never grows above baselineCount within 10 seconds.","commonSituations":"Douyin redesigned the cover-edit dialog so the label text changed; cover panel requires a click that lands on a covered element; slow page leaving the input disabled during the whole 10s window; modal (coach mark) blocking the cover entry.","solutions":["Retry — if the reason was 'cover-input-pending' the input may just have been slow to enable","Check whether the '上传新封面' label text changed on the live composer and update clis/douyin/draft.js:132","Dismiss any blocking modals first (they are only auto-dismissed before/after composer wait, not during cover prep)","Verify the cover image path is valid (bad path fails later, but a missing panel can stem from a page in a bad state — reload and rerun)"],"exampleFix":"// before: fixed label text\nconst coverLabel = ...find((el) => (el.textContent || '').includes('上传新封面'));\n// after: accept variants and increase patience\nconst coverLabel = ...find((el) => /上传新封面|自定义封面|修改封面/.test(el.textContent || ''));","handlingStrategy":"retry","validationCode":"// validate cover input before invoking\nconst cover = kwargs.cover;\nif (cover && !fs.existsSync(path.resolve(cover))) throw new Error('cover missing');","typeGuard":null,"tryCatchPattern":"try {\n  await opencli.douyin.draft({ video, title, cover });\n} catch (e) {\n  if (e.message.includes('准备抖音自定义封面输入框失败')) {\n    // retry once; if it persists, drop --cover and let Douyin auto-pick a frame\n    await opencli.douyin.draft({ video, title });\n  }\n}","preventionTips":["Dismiss modals before cover prep; coach marks block the '上传新封面' click","Use a standard-ratio JPEG cover to keep the panel flow simple","Fall back to no --cover (auto cover) if custom cover keeps failing"],"tags":["douyin","dom-selector","cover-upload","timeout"],"backgroundTag":"selector-not-found","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}