jackwener/OpenCLI · error · ArgumentError
operation must be one of: ${ACTION_CHOICES.join(', ')}
Error message
operation must be one of: ${ACTION_CHOICES.join(', ')} What it means
estimateAction validates the Midjourney operation name before computing its GPU-minute cost. If operation is not a member of ACTION_CHOICES, an ArgumentError is thrown listing all valid actions. This is a fail-fast guard so unsupported/typo'd subcommands never reach Playwright automation.
Source
Thrown at clis/midjourney/capabilities.js:296
if (estimatedMinutes > max) {
throw new ArgumentError(`Estimated GPU cost ${estimatedMinutes} minutes exceeds --max-minutes ${max}`);
}
const remainingCredits = Number(account?.total_credits ?? account?.credits_total);
const remainingMinutes = creditsToMinutes(remainingCredits);
if (Number.isFinite(remainingMinutes) && remainingMinutes - estimatedMinutes < reserve) {
throw new CommandExecutionError(
`Midjourney budget guard would leave ${Number((remainingMinutes - estimatedMinutes).toFixed(2))} minutes, below the ${reserve}-minute reserve`,
);
}
return { maxMinutes: max, reserveMinutes: reserve, remainingMinutes };
}
export function estimateAction(
operation,
{ videoResolution = 'sd', batchSize = 1, sourceIsVideo = false, sourceUsesOmni = false } = {},
) {
if (!ACTION_CHOICES.includes(operation)) {
throw new ArgumentError(`operation must be one of: ${ACTION_CHOICES.join(', ')}`);
}
if (operation === 'open-editor' || operation === 'cancel') return 0;
if (operation === 'rerun' && sourceIsVideo) {
if (!['sd', 'hd'].includes(videoResolution)) throw new ArgumentError('--video-resolution must be sd or hd');
if (![1, 2, 4].includes(batchSize)) throw new ArgumentError('--batch-size must be 1, 2, or 4');
return GPU_COST_MINUTES[`video${videoResolution === 'hd' ? 'Hd' : 'Sd'}${batchSize}`];
}
if (sourceUsesOmni && ['rerun', 'vary-subtle', 'vary-strong'].includes(operation)) {
return GPU_COST_MINUTES.omni;
}
if (Object.prototype.hasOwnProperty.call(ACTION_COSTS, operation)) return ACTION_COSTS[operation];
if (['animate-low', 'animate-high', 'loop-low', 'loop-high', 'extend-low', 'extend-high'].includes(operation)) {
if (!['sd', 'hd'].includes(videoResolution)) throw new ArgumentError('--video-resolution must be sd or hd');
if (![1, 2, 4].includes(batchSize)) throw new ArgumentError('--batch-size must be 1, 2, or 4');
return GPU_COST_MINUTES[`video${videoResolution === 'hd' ? 'Hd' : 'Sd'}${batchSize}`];
}
throw new ArgumentError(`No cost model is defined for action "${operation}"`);
}View on GitHub (pinned to 49907e53dc)
Solutions
- Check the error message's comma-separated list and use an exact supported operation string
- Add the new action to ACTION_CHOICES (and ACTION_COSTS or a cost branch) in clis/midjourney/capabilities.js if it is a legitimately new capability
- Trim/lowercase-and-normalize user input before calling estimateAction
- Wrap the CLI arg-parsing step to validate operation against ACTION_CHOICES before invoking estimateAction
Example fix
// before
const minutes = estimateAction('upscale', opts);
// after
if (!ACTION_CHOICES.includes('upscale')) throw new Error('unsupported');
const minutes = estimateAction('vary-subtle', opts); // use a listed action Defensive patterns
Strategy: validation
Validate before calling
import { ACTION_CHOICES, estimateAction } from './clis/midjourney/capabilities.js';
if (!ACTION_CHOICES.includes(operation)) {
throw new Error(`unsupported operation: ${operation}; expected one of ${ACTION_CHOICES.join(', ')}`);
}
const minutes = estimateAction(operation, opts); Type guard
function isMidjourneyAction(op) {
return typeof op === 'string' && ACTION_CHOICES.includes(op);
} Try / catch
try {
const minutes = estimateAction(operation, opts);
} catch (err) {
if (err instanceof ArgumentError && /operation must be one of/.test(err.message)) {
console.error(`Invalid operation "${operation}". Valid: ${ACTION_CHOICES.join(', ')}`);
process.exitCode = 2;
} else throw err;
} Prevention
- Always validate user-supplied action names against ACTION_CHOICES before calling estimateAction
- Normalize input (trim + toLowerCase) to catch whitespace/casing typos
- Keep ACTION_CHOICES and all cost branches in sync when adding actions
- Add a test enumerating every supported action through estimateAction
When it happens
Trigger: Calling estimateAction(operation) (directly or via estimated()) with a string not in ACTION_CHOICES, e.g. 'upscale', 'imagine-x', or a typo like 'vary-subtle ' with trailing whitespace or wrong casing.
Common situations: Typos in CLI subcommand wiring; renaming an action without updating call sites; passing a user-supplied action string straight through without pre-validation; adding a new action to a switch/caller but forgetting to add it to ACTION_CHOICES.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- archive search sort must be one of ${SORT_OPTIONS.join(', ')
- archive search mediatype must be one of ${MEDIATYPES.join(',
- archive search limit must be a positive integer
- archive search limit must be <= 100
- archive search query must not be empty
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/069b8aa097fa5676.
Report an issue: GitHub.