{"record":{"id":"8b502c0acaefae86","repo":"jackwener/OpenCLI","slug":"image-jobs-only-support-kind-image","errorCode":null,"errorMessage":"Image jobs only support --kind image","messagePattern":"Image jobs only support --kind image","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/midjourney/download.js","lineNumber":50,"sourceCode":"    { name: 'index', default: 'all', help: 'Candidate 1..4 or all' },\n    { name: 'kind', default: 'auto', help: 'auto, image, video-raw, video-social, or gif' },\n    { name: 'output', default: '~/Pictures/Midjourney', help: 'Output directory' },\n    { name: 'force', type: 'boolean', default: false, help: 'Overwrite existing non-empty files' },\n  ],\n  columns: ['job_id', 'status', 'kind', 'index', 'file', 'bytes', 'mime', 'url'],\n  func: async (page, kwargs) => {\n    const jobId = parseJobId(kwargs.job);\n    const job = await fetchJobStatus(page, jobId);\n    const status = String(job.current_status || job.status || '').toLowerCase();\n    if (status !== 'completed') {\n      throw new CommandExecutionError(`Midjourney job ${jobId} is \"${status || 'missing'}\"; media is available after completion.`);\n    }\n    const video = isVideoJob(job);\n    let kind = String(kwargs.kind || 'auto').trim().toLowerCase();\n    if (!KINDS.includes(kind)) throw new ArgumentError(`--kind must be one of: ${KINDS.join(', ')}`);\n    if (kind === 'auto') kind = video ? 'video-raw' : 'image';\n    if (video && kind === 'image') throw new ArgumentError('Video jobs support video-raw, video-social, or gif downloads');\n    if (!video && kind !== 'image') throw new ArgumentError('Image jobs only support --kind image');\n\n    const indices = parseImageIndices(kwargs.index, Number(job.batch_size || (video ? 1 : 4)));\n    const outputDir = resolveOutputDir(kwargs.output);\n    const force = normalizeBoolean(kwargs.force);\n    let files;\n    if (kind === 'image') {\n      files = (await downloadOriginals(page, jobId, indices, outputDir, force)).map((item) => ({\n        ...item,\n        kind: 'image',\n      }));\n    } else if (kind === 'video-raw') {\n      files = [];\n      for (const index of indices) files.push(await downloadRawVideo(page, jobId, index, outputDir, force));\n    } else {\n      files = [];\n      for (const index of indices) files.push(await downloadRenderedVideo(page, jobId, index, kind, outputDir, force));\n    }\n    return files.map((item) => ({","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/midjourney/download.js#L32-L68","documentation":"The `opencli midjourney download` command only downloads static images from image-generation jobs. After fetching the job via fetchJobStatus it calls isVideoJob(job); if the job is a normal image job but the user explicitly passed a video-related --kind (video-raw, video-social, or gif), this ArgumentError is thrown. Video-only download kinds simply do not exist for image jobs.","triggerScenarios":"Running `opencli midjourney download <job> --kind video-raw|video-social|gif` where the resolved job is NOT a video job (isVideoJob(job) returns false). The error only fires when the user explicitly selected a video kind for a non-video job, or reused a stale/saved --kind value; --kind auto always resolves correctly (video-raw for video jobs, image otherwise).","commonSituations":"Passing --kind video-raw out of habit from a previous video job; scripting with a hardcoded kind reused across a mixed list of jobs; assuming an image job with an animated preview supports GIF export; copy-pasting a download command from a video job to an image job.","solutions":["Drop the --kind flag (or use --kind auto) so image jobs download as images","Explicitly pass --kind image for this job","Verify the job is actually a video job with `opencli midjourney status <job>` before using video kinds","If you process a list of mixed jobs, branch on job type and only pass video kinds for video jobs"],"exampleFix":"// before\nopencli midjourney download abc123 --kind gif\n// after\nopencli midjourney download abc123 --kind image   # or omit --kind","handlingStrategy":"validation","validationCode":"const VIDEO_KINDS = ['video-raw', 'video-social', 'gif'];\nconst kind = (kwargs.kind || 'auto').trim().toLowerCase();\nconst jobIsVideo = /* from `opencli midjourney status <job>` output, e.g. job type contains 'video' */;\nif (!jobIsVideo && VIDEO_KINDS.includes(kind)) {\n  throw new Error(`Job is an image job; only --kind image is valid (got --kind ${kind})`);\n}","typeGuard":"function isVideoKind(kind) {\n  return ['video-raw', 'video-social', 'gif'].includes(String(kind).toLowerCase());\n}","tryCatchPattern":"try {\n  await cli('midjourney', 'download', job, { kind });\n} catch (e) {\n  if (/Image jobs only support --kind image/.test(e.message)) {\n    await cli('midjourney', 'download', job, { kind: 'image' });\n  } else throw e;\n}","preventionTips":["Omit --kind (default 'auto') unless you need a specific video format","Check `opencli midjourney status <job>` to confirm the job type before choosing a video kind","In scripts, derive the kind from the job type instead of hardcoding it","Only pass video kinds for jobs that came from video-capable generations"],"tags":["cli","argument-validation","midjourney","download"],"backgroundTag":"invalid-cli-argument","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}