{"record":{"id":"8dbaba1b0ea1e0bb","repo":"jackwener/OpenCLI","slug":"preparedimages-reason","errorCode":null,"errorMessage":"${preparedImages.reason}","messagePattern":"\\$\\{preparedImages\\.reason\\}","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/chatgpt/image.js","lineNumber":93,"sourceCode":"        { name: 'project', valueRequired: true, help: 'Start image generation inside a ChatGPT project ID or /g/g-p-<id> URL' },\n        { name: 'op', help: 'Output directory (default: ~/Pictures/chatgpt)' },\n        { name: 'sd', type: 'boolean', default: false, help: 'Skip download shorthand; only show ChatGPT link' },\n        { name: 'timeout', type: 'int', required: false, default: 240, help: 'Max seconds for the overall command (default: 240)' },\n    ],\n    columns: ['status', 'file', 'link'],\n    func: async (page, kwargs) => {\n        const prompt = kwargs.prompt;\n        const imagePaths = parseImagePaths(kwargs.image);\n        const outputDir = resolveOutputDir(kwargs.op);\n        const skipDownloadRaw = kwargs.sd;\n        const skipDownload = skipDownloadRaw === '' || skipDownloadRaw === true || normalizeBooleanFlag(skipDownloadRaw);\n        const timeout = kwargs.timeout;\n        if (!Number.isInteger(timeout) || timeout < 1) {\n            throw new ArgumentError('--timeout must be a positive integer (seconds)');\n        }\n        const preparedImages = imagePaths.length ? await prepareChatGPTImagePaths(imagePaths) : { ok: true, paths: [] };\n        if (!preparedImages.ok) {\n            throw new ArgumentError(preparedImages.reason);\n        }\n\n        // Navigate with full reload to clear React sidebar state before editing the draft.\n        if (kwargs.project) {\n            await navigateToProject(page, kwargs.project);\n        } else {\n            await page.goto(`https://${CHATGPT_DOMAIN}/new`, { settleMs: 2000 });\n        }\n        await clearChatGPTDraft(page);\n\n        if (imagePaths.length) {\n            let upload;\n            try {\n                upload = await uploadChatGPTImages(page, preparedImages.paths);\n            } catch (err) {\n                throw new CommandExecutionError(`Failed to upload image to ChatGPT: ${err instanceof Error ? err.message : String(err)}`);\n            }\n            if (!upload?.ok) throw new CommandExecutionError(upload?.reason || 'Failed to upload image to ChatGPT');","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/chatgpt/image.js#L75-L111","documentation":"When --image paths are supplied, the command calls prepareChatGPTImagePaths to resolve/validate each path; if it returns { ok: false, reason }, the command surfaces the reason verbatim as an ArgumentError. This means at least one image path could not be prepared (missing file, unreadable, unsupported, or not a valid local path).","triggerScenarios":"`opencli chatgpt image \"prompt\" --image /nonexistent.png` (or a comma-separated list where any entry fails) causing prepareChatGPTImagePaths to return ok:false with a reason string, which is thrown at image.js:93.","commonSituations":"Typos in file paths; file deleted or moved between planning and execution; relative paths resolved from an unexpected working directory; passing URLs instead of local files; missing read permissions.","solutions":["Read the reason in the error message — it names the offending path/problem.","Verify each --image path exists with ls (or fs.existsSync) and is readable.","Use absolute paths, comma-separated for multiple images: --image /abs/a.png,/abs/b.jpg.","Confirm the files are actual image files (png/jpg/webp/gif) the library supports."],"exampleFix":"// before\nopencli chatgpt image \"edit this\" --image ~/screenshots/shot.png\n# (file is actually at ~/Desktop/shot.png)\n// after\nopencli chatgpt image \"edit this\" --image ~/Desktop/shot.png","handlingStrategy":"validation","validationCode":"import fs from 'node:fs';\nconst imagePaths = 'a.png, b.jpg'.split(',').map(p => p.trim()).filter(Boolean);\nfor (const p of imagePaths) {\n  if (!fs.existsSync(p) || !fs.statSync(p).isFile()) {\n    throw new Error(`Image path not found: ${p}`);\n  }\n}","typeGuard":"const isExistingFile = (p) => { try { return fs.statSync(p).isFile(); } catch { return false; } };","tryCatchPattern":"try {\n  await run('chatgpt image', prompt, '--image', imagePaths.join(','));\n} catch (err) {\n  if (String(err.message).match(/no such file|not found|ENOENT/i)) {\n    console.error('Fix the --image path(s) reported in the message and retry');\n  } else throw err;\n}","preventionTips":["Stat every --image path with fs.existsSync before invoking","Use absolute paths to avoid working-directory surprises","Pass only local image files, not URLs","Keep paths free of stray whitespace/quotes when comma-joining"],"tags":["argument-validation","file-not-found","image-upload"],"backgroundTag":"invalid-input-file","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}