{"record":{"id":"6d5f806ae1d4e010","repo":"jackwener/OpenCLI","slug":"gif-jpg-jpeg-png-webp","errorCode":null,"errorMessage":"文字配图模式追加的图片不支持 .gif（编辑器图片入口只接受 jpg/jpeg/png/webp）","messagePattern":"文字配图模式追加的图片不支持 \\.gif（编辑器图片入口只接受 jpg/jpeg/png/webp）","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/xiaohongshu/publish.js","lineNumber":1211,"sourceCode":"        const cards = cardText\n            ? cardText.split(CARD_TEXT_DELIM).map((s) => s.trim()).filter(Boolean)\n            : [];\n        const cardStyle = kwargs['card-style'] ? String(kwargs['card-style']).trim() : '';\n        const isTextImage = cards.length > 0;\n        // ── Validate inputs ────────────────────────────────────────────────────────\n        if (!title)\n            throw new ArgumentError('--title is required');\n        if (title.length > MAX_TITLE_LEN)\n            throw new ArgumentError(`Title is ${title.length} chars — must be ≤ ${MAX_TITLE_LEN}`);\n        if (!content)\n            throw new ArgumentError('Positional argument <content> is required');\n        if (!isTextImage && imagePaths.length === 0)\n            throw new ArgumentError('Provide --card-text (text-image mode) or --images (upload mode); neither was given.');\n        if (imagePaths.length > MAX_IMAGES)\n            throw new ArgumentError(`Too many images: ${imagePaths.length} (max ${MAX_IMAGES})`);\n        // The editor-page image input (text-image append) rejects gif.\n        if (isTextImage && imagePaths.some((p) => path.extname(p).toLowerCase() === '.gif'))\n            throw new ArgumentError('文字配图模式追加的图片不支持 .gif（编辑器图片入口只接受 jpg/jpeg/png/webp）');\n        // Validate image paths before navigating (fast-fail on bad paths / unsupported formats)\n        const absImagePaths = validateImagePaths(imagePaths);\n        // ── Step 1: Navigate to publish page ──────────────────────────────────────\n        await page.goto(PUBLISH_URL);\n        await page.wait({ time: 3 });\n        // Verify we landed on the creator site (not redirected to login)\n        const pageUrl = await page.evaluate('() => location.href');\n        if (!pageUrl.includes('creator.xiaohongshu.com')) {\n            throw new Error('Redirected away from creator center — session may have expired. ' +\n                'Re-capture browser login via: opencli xiaohongshu creator-profile');\n        }\n        // ── Step 2: Select 图文 (image+text) note type if tabs are present ─────────\n        const tabResult = await selectImageTextTab(page);\n        const surface = await waitForPublishSurfaceState(page, tabResult?.ok ? 5_000 : 2_000);\n        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}\"`","sourceCodeStart":1193,"sourceCodeEnd":1229,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/xiaohongshu/publish.js#L1193-L1229","documentation":"ArgumentError thrown when text-image (文字配图) mode is used but one of the supplied --images files has a .gif extension. The editor-page image input on XHS only accepts jpg/jpeg/png/webp, so the CLI rejects gif up front rather than failing mid-automation.","triggerScenarios":"Running publish with --card-text (isTextImage true) where any path in --images ends with '.gif' (case-insensitive extension check).","commonSituations":"Exporting animated stickers/memes as gifs and mixing them into the image list; screenshot tools defaulting to gif; not knowing the XHS text-image editor's format restriction (it differs from the plain upload flow).","solutions":["Convert the .gif to .png or .webp (e.g. ffmpeg -i in.gif out.png or magick in.gif out.png)","Remove .gif files from the --images list","For animated content, use upload mode --images without --card-text, or post as a video instead"],"exampleFix":"// before\n--card-text \"a|b\" --images \"./meme.gif\"\n// after\nmagick ./meme.gif ./meme.png\n--card-text \"a|b\" --images \"./meme.png\"","handlingStrategy":"validation","validationCode":"const ALLOWED = ['.jpg', '.jpeg', '.png', '.webp'];\nconst bad = imagePaths.filter(p => !ALLOWED.includes(path.extname(p).toLowerCase()));\nif (bad.length) throw new Error(`Unsupported formats: ${bad.join(', ')}`);","typeGuard":"function isEditorSupportedImage(p) {\n  return ['.jpg', '.jpeg', '.png', '.webp'].includes(path.extname(p).toLowerCase());\n}","tryCatchPattern":"try {\n  await publish(page, { 'card-text': cards, images: imagesFlag, ...rest });\n} catch (e) {\n  if (String(e.message).includes('.gif')) {\n    console.error('Convert gif to png/webp before text-image publishing');\n  }\n  throw e;\n}","preventionTips":["Filter or convert gifs out of image lists before publishing","Standardize exports to jpg/png/webp","Pre-validate extensions for any programmatic image pipeline feeding the CLI"],"tags":["validation","file-format","gif","xiaohongshu"],"backgroundTag":"unsupported-file-format","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}