{"record":{"id":"474abc2bfc863af7","repo":"jackwener/OpenCLI","slug":"label-expected-at-least-expectedcount-visib","errorCode":null,"errorMessage":"${label}: expected at least ${expectedCount} visible media item(s), got ${state.count}. Debug screenshot: /tmp/xhs_publish_media_debug.png","messagePattern":"(.+?): expected at least (.+?) visible media item\\(s\\), got (.+?)\\. Debug screenshot: /tmp/xhs_publish_media_debug\\.png","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/publish.js","lineNumber":1042,"sourceCode":"          const key = src || String(Math.round(rect.left)) + ':' + String(Math.round(rect.top));\n          if (seen.has(key)) continue;\n          seen.add(key);\n          count += 1;\n        }\n      }\n      return { ok: true, count };\n    })()\n  `);\n    return unwrapBrowserResult(result);\n}\nasync function assertComposerMediaCount(page, expectedCount, label) {\n    const state = await currentComposerMediaCount(page);\n    if (!state || typeof state.count !== 'number') {\n        throw new CommandExecutionError(`${label}: could not verify current composer media count`);\n    }\n    if (state.count < expectedCount) {\n        await page.screenshot({ path: '/tmp/xhs_publish_media_debug.png' });\n        throw new CommandExecutionError(`${label}: expected at least ${expectedCount} visible media item(s), got ${state.count}. ` +\n            'Debug screenshot: /tmp/xhs_publish_media_debug.png');\n    }\n}\n/**\n * Drive the full 文字配图 sub-flow: entry → type cards → 生成图片 → pick style → 下一步.\n * Leaves the page on the standard editor (caller then runs waitForEditForm).\n */\nasync function runTextImageFlow(page, cards, cardStyle) {\n    const entry = await clickByText(page, TEXT_IMAGE_ENTRY_LABEL);\n    if (!entry?.ok) {\n        await page.screenshot({ path: '/tmp/xhs_publish_textimage_debug.png' });\n        throw new CommandExecutionError(`文字配图: could not click \"${TEXT_IMAGE_ENTRY_LABEL}\" entry. ` +\n            'Debug: /tmp/xhs_publish_textimage_debug.png');\n    }\n    if (!(await waitForFirstCard(page))) {\n        await page.screenshot({ path: '/tmp/xhs_publish_textimage_debug.png' });\n        throw new CommandExecutionError(`文字配图: 写文字 card editor did not appear after clicking \"${TEXT_IMAGE_ENTRY_LABEL}\". ` +\n            'Debug: /tmp/xhs_publish_textimage_debug.png');","sourceCodeStart":1024,"sourceCodeEnd":1060,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/publish.js#L1024-L1060","documentation":"assertComposerMediaCount verified the count successfully but found fewer visible media items than expected. It saves a debug screenshot to /tmp/xhs_publish_media_debug.png and throws with both counts. This is a write-postcondition guard so media uploads that silently failed are caught before publishing.","triggerScenarios":"state.count < expectedCount after uploading media: one or more uploads failed silently, media still processing/not yet rendered in the composer, or a duplicate/blank upload was dropped by XHS.","commonSituations":"Large images timing out during upload; network throttling so thumbnails haven't rendered within the wait window; XHS rejecting an image (format/size) without surfacing an error; uploading more images than XHS's limit.","solutions":["Open /tmp/xhs_publish_media_debug.png to see what the composer actually shows and which media is missing.","Retry the publish with longer waits between uploads so processing finishes before assertion.","Validate images before upload (format, size limits) and reduce count if exceeding XHS's max (e.g. 18 for image posts).","Re-upload the failed media; if one specific image always fails, re-encode it (JPEG, within size limit)."],"exampleFix":"// before\nawait uploadMedia(page, images);\nawait assertComposerMediaCount(page, images.length, \"发布\");\n// after\nfor (const img of images) {\n  await uploadOne(page, img);\n  await page.wait({ time: 1.5 }); // let thumbnail render\n}\nawait assertComposerMediaCount(page, images.length, \"发布\");","handlingStrategy":"validation","validationCode":"// check each image before upload\nfor (const img of images) {\n  const kb = fs.statSync(img).size / 1024;\n  if (kb > 32000) throw new Error(`${img} exceeds XHS size limit`);\n  if (!/\\.(jpe?g|png|webp)$/i.test(img)) throw new Error(`${img} unsupported format`);\n}\nif (images.length > 18) throw new Error('XHS allows max 18 images per post');","typeGuard":"null","tryCatchPattern":"try { await assertComposerMediaCount(page, images.length, '发布'); }\ncatch (e) {\n  if (/expected at least/.test(e.message)) {\n    console.error('Check debug screenshot: /tmp/xhs_publish_media_debug.png');\n    // re-upload missing media and re-assert\n  } else throw e;\n}","preventionTips":["Pre-validate image format, size, and count against XHS limits","Wait between uploads so thumbnails render before counting","Always inspect the debug screenshot path in the message"],"tags":["upload","media","postcondition-failed"],"backgroundTag":"media-upload-incomplete","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}