{"record":{"id":"739b813abbb21933","repo":"jackwener/OpenCLI","slug":"image-injection-failed-upload-error-unknown","errorCode":null,"errorMessage":"Image injection failed: ${upload.error ?? 'unknown'}. Debug screenshot: /tmp/xhs_publish_upload_debug.png","messagePattern":"Image injection failed: (.+?)\\. Debug screenshot: /tmp/xhs_publish_upload_debug\\.png","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/publish.js","lineNumber":1244,"sourceCode":"        if (surface.state === 'video_surface') {\n            await page.screenshot({ path: '/tmp/xhs_publish_tab_debug.png' });\n            const detail = tabResult?.ok\n                ? `clicked \"${tabResult.text}\"`\n                : `visible candidates: ${(tabResult?.visibleTexts || []).join(' | ') || 'none'}`;\n            throw new Error('Still on the video publish page after trying to select 图文. ' +\n                `Details: ${detail}. Debug screenshot: /tmp/xhs_publish_tab_debug.png`);\n        }\n        // ── Step 3: Acquire images — text-image generation and/or upload ──────────\n        let appliedCardStyle = cardStyle;\n        if (isTextImage) {\n            // Drive 文字配图: type cards → 生成图片 → pick style → 下一步 → standard editor.\n            appliedCardStyle = await runTextImageFlow(page, cards, cardStyle);\n        }\n        else {\n            const upload = await uploadImages(page, absImagePaths);\n            if (!upload.ok) {\n                await page.screenshot({ path: '/tmp/xhs_publish_upload_debug.png' });\n                throw new CommandExecutionError(`Image injection failed: ${upload.error ?? 'unknown'}. ` +\n                    'Debug screenshot: /tmp/xhs_publish_upload_debug.png');\n            }\n            await page.wait({ time: UPLOAD_SETTLE_MS / 1_000 });\n            await waitForUploads(page);\n        }\n        // ── Step 3b: Wait for editor form to render ───────────────────────────────\n        const formReady = await waitForEditForm(page);\n        if (!formReady) {\n            await page.screenshot({ path: '/tmp/xhs_publish_form_debug.png' });\n            throw new CommandExecutionError('Editing form did not appear after image acquisition. The page layout may have changed. ' +\n                'Debug screenshot: /tmp/xhs_publish_form_debug.png');\n        }\n        if (isTextImage) {\n            await assertComposerMediaCount(page, cards.length, '文字配图 generated images');\n        }\n        // ── Step 3c: In text-image mode, optionally append uploaded images ────────\n        if (isTextImage && absImagePaths.length > 0) {\n            const upload = await uploadImages(page, absImagePaths);","sourceCodeStart":1226,"sourceCodeEnd":1262,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/publish.js#L1226-L1262","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nopencli xiaohongshu publish --images ./shot.png\n// after\nopencli xiaohongshu publish --images \"$(pwd)/shot.png\"   # absolute, verified path","handlingStrategy":"validation","validationCode":"const fs = require('fs');\nfor (const p of imagePaths) {\n  if (!fs.existsSync(p)) throw new Error(`Image not found: ${p}`);\n  if (!/\\.(jpe?g|png|webp)$/i.test(p)) throw new Error(`Unsupported format: ${p}`);\n}","typeGuard":"function isUploadableImage(p) {\n  return typeof p === 'string' && path.isAbsolute(p) &&\n    fs.existsSync(p) && /\\.(jpe?g|png|webp)$/i.test(p);\n}","tryCatchPattern":"try {\n  await publish({ images: paths });\n} catch (e) {\n  if (String(e.message).startsWith('Image injection failed')) {\n    console.error('Check /tmp/xhs_publish_upload_debug.png and verify image paths');\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["file-upload","io","browser-automation","xiaohongshu"],"backgroundTag":"file-upload-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}