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

  1. Check the error message's comma-separated list and use an exact supported operation string
  2. 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
  3. Trim/lowercase-and-normalize user input before calling estimateAction
  4. 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

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


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/069b8aa097fa5676. Report an issue: GitHub.