jackwener/OpenCLI · error · CommandExecutionError

Image injection failed: ${upload.error ?? 'unknown'}. Debug

Error message

Image injection failed: ${upload.error ?? 'unknown'}. Debug screenshot: /tmp/xhs_publish_upload_debug.png

What it means

uploadImages() failed while injecting the local image files into the publish composer's file input. The CLI takes a debug screenshot and aborts, surfacing the inner upload.error (or 'unknown' if none).

Source

Thrown at clis/xiaohongshu/publish.js:1244

        if (surface.state === 'video_surface') {
            await page.screenshot({ path: '/tmp/xhs_publish_tab_debug.png' });
            const detail = tabResult?.ok
                ? `clicked "${tabResult.text}"`
                : `visible candidates: ${(tabResult?.visibleTexts || []).join(' | ') || 'none'}`;
            throw new Error('Still on the video publish page after trying to select 图文. ' +
                `Details: ${detail}. Debug screenshot: /tmp/xhs_publish_tab_debug.png`);
        }
        // ── Step 3: Acquire images — text-image generation and/or upload ──────────
        let appliedCardStyle = cardStyle;
        if (isTextImage) {
            // Drive 文字配图: type cards → 生成图片 → pick style → 下一步 → standard editor.
            appliedCardStyle = await runTextImageFlow(page, cards, cardStyle);
        }
        else {
            const upload = await uploadImages(page, absImagePaths);
            if (!upload.ok) {
                await page.screenshot({ path: '/tmp/xhs_publish_upload_debug.png' });
                throw new CommandExecutionError(`Image injection failed: ${upload.error ?? 'unknown'}. ` +
                    'Debug screenshot: /tmp/xhs_publish_upload_debug.png');
            }
            await page.wait({ time: UPLOAD_SETTLE_MS / 1_000 });
            await waitForUploads(page);
        }
        // ── Step 3b: Wait for editor form to render ───────────────────────────────
        const formReady = await waitForEditForm(page);
        if (!formReady) {
            await page.screenshot({ path: '/tmp/xhs_publish_form_debug.png' });
            throw new CommandExecutionError('Editing form did not appear after image acquisition. The page layout may have changed. ' +
                'Debug screenshot: /tmp/xhs_publish_form_debug.png');
        }
        if (isTextImage) {
            await assertComposerMediaCount(page, cards.length, '文字配图 generated images');
        }
        // ── Step 3c: In text-image mode, optionally append uploaded images ────────
        if (isTextImage && absImagePaths.length > 0) {
            const upload = await uploadImages(page, absImagePaths);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Check the upload.error detail in the message and /tmp/xhs_publish_upload_debug.png.
  2. Verify each image path exists, is absolute, and is a supported format (jpg/png/webp).
  3. Retry — transient page-load races can break injection.
  4. If paths are fine, the composer DOM likely changed; update the CLI or report the layout change.

Example fix

// before
opencli xiaohongshu publish --images ./shot.png
// after
opencli xiaohongshu publish --images "$(pwd)/shot.png"   # absolute, verified path
Defensive patterns

Strategy: validation

Validate before calling

const fs = require('fs');
for (const p of imagePaths) {
  if (!fs.existsSync(p)) throw new Error(`Image not found: ${p}`);
  if (!/\.(jpe?g|png|webp)$/i.test(p)) throw new Error(`Unsupported format: ${p}`);
}

Type guard

function isUploadableImage(p) {
  return typeof p === 'string' && path.isAbsolute(p) &&
    fs.existsSync(p) && /\.(jpe?g|png|webp)$/i.test(p);
}

Try / catch

try {
  await publish({ images: paths });
} catch (e) {
  if (String(e.message).startsWith('Image injection failed')) {
    console.error('Check /tmp/xhs_publish_upload_debug.png and verify image paths');
  }
  throw e;
}

Prevention

When it happens

Trigger: uploadImages returns { ok:false } — e.g. an image path does not exist/is not readable, the file input selector changed, or the DOM upload injection was rejected by the page.

Common situations: Passing a relative path that was not resolved to absImagePaths, a corrupt/unsupported image format, image deleted between listing and upload, or xiaohongshu DOM changes breaking the input[type=file] hook.

Related errors


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