{"record":{"id":"ebe6fa52457c1b95","repo":"jackwener/OpenCLI","slug":"video-resolution-must-be-sd-or-hd","errorCode":null,"errorMessage":"--video-resolution must be sd or hd","messagePattern":"--video-resolution must be sd or hd","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"clis/midjourney/capabilities.js","lineNumber":300,"sourceCode":"  const remainingMinutes = creditsToMinutes(remainingCredits);\n  if (Number.isFinite(remainingMinutes) && remainingMinutes - estimatedMinutes < reserve) {\n    throw new CommandExecutionError(\n      `Midjourney budget guard would leave ${Number((remainingMinutes - estimatedMinutes).toFixed(2))} minutes, below the ${reserve}-minute reserve`,\n    );\n  }\n  return { maxMinutes: max, reserveMinutes: reserve, remainingMinutes };\n}\n\nexport function estimateAction(\n  operation,\n  { videoResolution = 'sd', batchSize = 1, sourceIsVideo = false, sourceUsesOmni = false } = {},\n) {\n  if (!ACTION_CHOICES.includes(operation)) {\n    throw new ArgumentError(`operation must be one of: ${ACTION_CHOICES.join(', ')}`);\n  }\n  if (operation === 'open-editor' || operation === 'cancel') return 0;\n  if (operation === 'rerun' && sourceIsVideo) {\n    if (!['sd', 'hd'].includes(videoResolution)) throw new ArgumentError('--video-resolution must be sd or hd');\n    if (![1, 2, 4].includes(batchSize)) throw new ArgumentError('--batch-size must be 1, 2, or 4');\n    return GPU_COST_MINUTES[`video${videoResolution === 'hd' ? 'Hd' : 'Sd'}${batchSize}`];\n  }\n  if (sourceUsesOmni && ['rerun', 'vary-subtle', 'vary-strong'].includes(operation)) {\n    return GPU_COST_MINUTES.omni;\n  }\n  if (Object.prototype.hasOwnProperty.call(ACTION_COSTS, operation)) return ACTION_COSTS[operation];\n  if (['animate-low', 'animate-high', 'loop-low', 'loop-high', 'extend-low', 'extend-high'].includes(operation)) {\n    if (!['sd', 'hd'].includes(videoResolution)) throw new ArgumentError('--video-resolution must be sd or hd');\n    if (![1, 2, 4].includes(batchSize)) throw new ArgumentError('--batch-size must be 1, 2, or 4');\n    return GPU_COST_MINUTES[`video${videoResolution === 'hd' ? 'Hd' : 'Sd'}${batchSize}`];\n  }\n  throw new ArgumentError(`No cost model is defined for action \"${operation}\"`);\n}\n\nexport function assertActionPlan(account, operation, { videoResolution = 'sd' } = {}) {\n  const capabilities = planCapabilities(account);\n  if (videoResolution === 'hd' && !capabilities.canHdVideo","sourceCodeStart":282,"sourceCodeEnd":318,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/midjourney/capabilities.js#L282-L318","documentation":"When rerunning a video source (sourceIsVideo), estimateAction requires videoResolution to be exactly 'sd' or 'hd' so it can index GPU_COST_MINUTES. Any other value throws this ArgumentError before any cost lookup happens.","triggerScenarios":"estimateAction('rerun', { sourceIsVideo: true, videoResolution: '4k' }) — or any value other than 'sd'/'hd', including null/undefined passed explicitly or an uppercase 'HD'.","commonSituations":"Users passing --video-resolution with values like 'high', '1080p', or 'hd '; config files carrying an older allowed value set; case-sensitive comparison against 'HD'.","solutions":["Pass videoResolution: 'sd' or 'hd' (lowercase) when calling estimateAction","Normalize input: String(v).trim().toLowerCase() before the call","Fix the CLI/config default so an invalid value never reaches estimateAction","Guard the call site: only call with sourceIsVideo=true after validating resolution"],"exampleFix":"// before\nestimateAction('rerun', { sourceIsVideo: true, videoResolution: 'HD' });\n// after\nconst v = String(opts.videoResolution || 'sd').toLowerCase();\nif (v !== 'sd' && v !== 'hd') throw new Error('--video-resolution must be sd or hd');\nestimateAction('rerun', { sourceIsVideo: true, videoResolution: v });","handlingStrategy":"validation","validationCode":"const v = String(opts.videoResolution ?? 'sd').trim().toLowerCase();\nif (v !== 'sd' && v !== 'hd') throw new Error(`--video-resolution must be sd or hd, got ${opts.videoResolution}`);\nconst minutes = estimateAction('rerun', { ...opts, videoResolution: v, sourceIsVideo: true });","typeGuard":"function isVideoResolution(v) {\n  return v === 'sd' || v === 'hd';\n}","tryCatchPattern":"try {\n  const minutes = estimateAction('rerun', { sourceIsVideo: true, videoResolution, batchSize });\n} catch (err) {\n  if (err instanceof ArgumentError && err.message.includes('--video-resolution')) {\n    console.error('Use --video-resolution sd or hd');\n  } else throw err;\n}","preventionTips":["Coerce resolution with trim().toLowerCase() at the CLI boundary","Default to 'sd' when the flag is absent","Document allowed values in --help","Validate before calling estimateAction rather than relying on its throw"],"tags":["argument-error","validation","cli","video"],"backgroundTag":"invalid-argument-value","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}