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
- Check the upload.error detail in the message and /tmp/xhs_publish_upload_debug.png.
- Verify each image path exists, is absolute, and is a supported format (jpg/png/webp).
- Retry — transient page-load races can break injection.
- 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
- Always pass absolute, existing image paths (path.resolve before invoking).
- Use supported formats (jpg/png/webp) and reasonably sized files.
- Don't delete or move image files between listing and publishing.
- Check /tmp/xhs_publish_upload_debug.png when failures persist.
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
- Appending images failed: ${upload.error ?? 'unknown'}. Debug
- Failed to attach file
- Instagram fallback file injection failed
- Mercury reimbursement-draft requires Browser Bridge uploadFi
- Mercury receipt upload did not confirm exactly one uploaded
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/739b813abbb21933.
Report an issue: GitHub.