{"record":{"id":"990f3841181006c9","repo":"jackwener/OpenCLI","slug":"math-ceil-maxms-1000","errorCode":null,"errorMessage":"视频上传/转码超时（${Math.ceil(maxMs / 1000)}秒），请检查网络或稍后重试","messagePattern":"视频上传/转码超时（(.+?)秒），请检查网络或稍后重试","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/wechat-channels/publish.js","lineNumber":314,"sourceCode":"      // Wait and retry rather than aborting.\n      const msg = err instanceof Error ? err.message : String(err);\n      if (msg.includes('ECONNRESET') || msg.includes('fetch failed')) {\n        process.stderr.write(`  [retry] bridge reconnecting after page re-render (${i + 1}/${maxAttempts})...\\n`);\n        await page.wait({ time: pollMs / 1000 });\n        continue;\n      }\n      throw err;\n    }\n\n    if (done?.failed) {\n      throw new CommandExecutionError('视频上传失败，请检查文件格式和网络连接');\n    }\n    if (done?.done) return;\n\n    await page.wait({ time: pollMs / 1000 });\n  }\n\n  throw new CommandExecutionError(`视频上传/转码超时（${Math.ceil(maxMs / 1000)}秒），请检查网络或稍后重试`);\n}\n\n// ── Helper: fill text field (with shadow DOM traversal) ─────────────────────\nasync function fillField(page, selectors, text, fieldName) {\n  const result = await evalPage(page, `\n    (function(selectors, text) {\n      ${DEEP_QUERY_FN}\n\n      var el = null;\n      var foundSel = null;\n      for (var i = 0; i < selectors.length; i++) {\n        var candidate = deepQuery(selectors[i]);\n        if (candidate && isVisible(candidate)) {\n          el = candidate;\n          foundSel = selectors[i];\n          break;\n        }\n      }","sourceCodeStart":296,"sourceCodeEnd":332,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/wechat-channels/publish.js#L296-L332","documentation":"waitForUploadDone polls every 3 seconds up to maxMs (default 180s, bounded by the --timeout deadline); if the upload/transcode never completes in that window it throws CommandExecutionError with the elapsed seconds and asks the user to check the network or retry. Unlike 4407, the page never reported failure — it simply never reported done.","triggerScenarios":"Upload throughput too low for the file size within maxMs; transcode stuck server-side; page status element changed so the completion check never matches; extremely large 4K files.","commonSituations":"Slow or throttled uplinks; WeChat-side transcode queues busy; WeChat DOM change breaking status detection so 'done' is never observed even though upload finished.","solutions":["Retry with a longer window — increase --timeout so the wait budget grows.","Upload a smaller or lower-bitrate video.","Verify network stability, then retry.","If the file actually uploaded (visible on the page), the status selectors in waitForUploadDone may need updating for a WeChat DOM change."],"exampleFix":"// before\nnode publish.js --video 4k-master.mp4 --timeout 120\n// after\nnode publish.js --video 4k-master.mp4 --timeout 1800","handlingStrategy":"retry","validationCode":"const mb = require('fs').statSync(videoPath).size / 1048576;\nif (mb > 500 && timeoutSeconds < 1800) {\n  console.warn(`Large file (${mb.toFixed(0)}MB); consider --timeout >= 1800`);\n}","typeGuard":"null","tryCatchPattern":"try {\n  await publish({ videoPath, timeout });\n} catch (e) {\n  if (/视频上传\\/转码超时/.test(e.message)) {\n    return publish({ videoPath, timeout: Math.max(timeout * 2, 1800) });\n  }\n  throw e;\n}","preventionTips":["Budget --timeout proportionally to file size and uplink speed.","Prefer smaller/bitrated encodes for large source videos.","If uploads complete but the tool never notices, check for WeChat DOM changes in status polling."],"tags":["timeout","upload","network"],"backgroundTag":"upload-timeout","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}