jackwener/OpenCLI · error · CommandExecutionError

Appending images failed: ${upload.error ?? 'unknown'}. Debug

Error message

Appending images failed: ${upload.error ?? 'unknown'}. Debug screenshot: /tmp/xhs_publish_append_debug.png

What it means

In 文字配图 (text-image) mode, after generated cards are applied, any additional --images files are appended via uploadImages. If that append upload fails, the CLI screenshots the page and aborts with the underlying upload error.

Source

Thrown at clis/xiaohongshu/publish.js:1265

            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);
            if (!upload.ok) {
                await page.screenshot({ path: '/tmp/xhs_publish_append_debug.png' });
                throw new CommandExecutionError(`Appending images failed: ${upload.error ?? 'unknown'}. ` +
                    'Debug screenshot: /tmp/xhs_publish_append_debug.png');
            }
            await page.wait({ time: UPLOAD_SETTLE_MS / 1_000 });
            await waitForUploads(page);
            await assertComposerMediaCount(page, cards.length + absImagePaths.length, '文字配图 appended images');
        }
        // ── Step 4: Fill title ─────────────────────────────────────────────────────
        await fillField(page, TITLE_SELECTORS, title, 'title');
        await page.wait({ time: 0.5 });
        // ── Step 5: Fill content / body ────────────────────────────────────────────
        await fillField(page, BODY_SELECTORS, content, 'content');
        await page.wait({ time: 0.5 });
        // ── Step 6: Add topic hashtags ─────────────────────────────────────────────
        // XHS converts a "#keyword" typed into the body editor into a real topic
        // entity only when the user picks an item from the inline suggestion
        // dropdown that pops up while typing. The previous implementation looked
        // for a standalone "添加话题" button + dedicated search <input>, which the
        // current creator-center editor no longer exposes — it left bare "#"

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Read the upload.error detail and check /tmp/xhs_publish_append_debug.png.
  2. Verify each appended image path exists, is absolute, and is a supported format.
  3. Retry the command; transient injection races occur.
  4. If only generated cards are needed, drop the --images flag to bypass the append step.

Example fix

// before
opencli xiaohongshu publish --text-image --images missing.png
// after
ls -l missing.png   # verify first, or omit --images to use generated cards only
Defensive patterns

Strategy: validation

Validate before calling

const fs = require('fs');
const appendPaths = opts.images || [];
for (const p of appendPaths) {
  if (!fs.existsSync(p)) throw new Error(`Append image not found: ${p}`);
}

Try / catch

try {
  await publish({ textImage: true, images: extra });
} catch (e) {
  if (String(e.message).startsWith('Appending images failed')) {
    // fall back to generated cards only
    return publish({ textImage: true });
  }
  throw e;
}

Prevention

When it happens

Trigger: isTextImage is true and absImagePaths.length > 0, and the second uploadImages call returns { ok:false } — file missing/unreadable, input selector changed, or injection rejected.

Common situations: Same causes as the initial upload error: bad/relative image paths, unsupported format, deleted files, or composer DOM changes — but hit only when combining generated cards with extra uploaded images.

Related errors


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