jackwener/OpenCLI · error · CommandExecutionError

${label}: expected at least ${expectedCount} visible media i

Error message

${label}: expected at least ${expectedCount} visible media item(s), got ${state.count}. Debug screenshot: /tmp/xhs_publish_media_debug.png

What it means

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.

Source

Thrown at clis/xiaohongshu/publish.js:1042

          const key = src || String(Math.round(rect.left)) + ':' + String(Math.round(rect.top));
          if (seen.has(key)) continue;
          seen.add(key);
          count += 1;
        }
      }
      return { ok: true, count };
    })()
  `);
    return unwrapBrowserResult(result);
}
async function assertComposerMediaCount(page, expectedCount, label) {
    const state = await currentComposerMediaCount(page);
    if (!state || typeof state.count !== 'number') {
        throw new CommandExecutionError(`${label}: could not verify current composer media count`);
    }
    if (state.count < expectedCount) {
        await page.screenshot({ path: '/tmp/xhs_publish_media_debug.png' });
        throw new CommandExecutionError(`${label}: expected at least ${expectedCount} visible media item(s), got ${state.count}. ` +
            'Debug screenshot: /tmp/xhs_publish_media_debug.png');
    }
}
/**
 * Drive the full 文字配图 sub-flow: entry → type cards → 生成图片 → pick style → 下一步.
 * Leaves the page on the standard editor (caller then runs waitForEditForm).
 */
async function runTextImageFlow(page, cards, cardStyle) {
    const entry = await clickByText(page, TEXT_IMAGE_ENTRY_LABEL);
    if (!entry?.ok) {
        await page.screenshot({ path: '/tmp/xhs_publish_textimage_debug.png' });
        throw new CommandExecutionError(`文字配图: could not click "${TEXT_IMAGE_ENTRY_LABEL}" entry. ` +
            'Debug: /tmp/xhs_publish_textimage_debug.png');
    }
    if (!(await waitForFirstCard(page))) {
        await page.screenshot({ path: '/tmp/xhs_publish_textimage_debug.png' });
        throw new CommandExecutionError(`文字配图: 写文字 card editor did not appear after clicking "${TEXT_IMAGE_ENTRY_LABEL}". ` +
            'Debug: /tmp/xhs_publish_textimage_debug.png');

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Open /tmp/xhs_publish_media_debug.png to see what the composer actually shows and which media is missing.
  2. Retry the publish with longer waits between uploads so processing finishes before assertion.
  3. Validate images before upload (format, size limits) and reduce count if exceeding XHS's max (e.g. 18 for image posts).
  4. Re-upload the failed media; if one specific image always fails, re-encode it (JPEG, within size limit).

Example fix

// before
await uploadMedia(page, images);
await assertComposerMediaCount(page, images.length, "发布");
// after
for (const img of images) {
  await uploadOne(page, img);
  await page.wait({ time: 1.5 }); // let thumbnail render
}
await assertComposerMediaCount(page, images.length, "发布");
Defensive patterns

Strategy: validation

Validate before calling

// check each image before upload
for (const img of images) {
  const kb = fs.statSync(img).size / 1024;
  if (kb > 32000) throw new Error(`${img} exceeds XHS size limit`);
  if (!/\.(jpe?g|png|webp)$/i.test(img)) throw new Error(`${img} unsupported format`);
}
if (images.length > 18) throw new Error('XHS allows max 18 images per post');

Type guard

null

Try / catch

try { await assertComposerMediaCount(page, images.length, '发布'); }
catch (e) {
  if (/expected at least/.test(e.message)) {
    console.error('Check debug screenshot: /tmp/xhs_publish_media_debug.png');
    // re-upload missing media and re-assert
  } else throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/474abc2bfc863af7. Report an issue: GitHub.