bmad-code-org/BMAD-METHOD · error · Error
Invalid action: ${options.action}. Valid actions: ${validAct
Error message
Invalid action: ${options.action}. Valid actions: ${validActions.join(', ')} What it means
Thrown when --action <value> is passed but the value isn't in the dynamically-built choices list. Choices depend on install state: 'update' is always present for an existing install; 'quick-update' only appears when an existing install is detected. The valid set is printed in the message.
Source
Thrown at tools/installer/ui.js:293
// Build menu choices dynamically
const choices = [];
// Always show Quick Update first (allows refreshing installation even on same version)
if (existingInstall.installed) {
choices.push({
name: 'Quick Update',
value: 'quick-update',
});
}
// Common actions
choices.push({ name: 'Modify BMAD Installation', value: 'update' });
// Check if action is provided via command-line
if (options.action) {
const validActions = choices.map((c) => c.value);
if (!validActions.includes(options.action)) {
throw new Error(`Invalid action: ${options.action}. Valid actions: ${validActions.join(', ')}`);
}
actionType = options.action;
await prompts.log.info(`Using action from command-line: ${actionType}`);
} else if (options.yes) {
// Default to quick-update if available, unless flags that require the
// full update path are present (e.g. --custom-source which re-clones
// modules at a new version — quick-update skips that entirely).
if (choices.length === 0) {
throw new Error('No valid actions available for this installation');
}
const hasQuickUpdate = choices.some((c) => c.value === 'quick-update');
const needsFullUpdate = !!options.customSource;
actionType = hasQuickUpdate && !needsFullUpdate ? 'quick-update' : (choices.find((c) => c.value === 'update') || choices[0]).value;
await prompts.log.info(`Non-interactive mode (--yes): defaulting to ${actionType}`);
} else {
actionType = await prompts.select({
message: 'How would you like to proceed?',
choices: choices,View on GitHub (pinned to b70486b9bd)
Solutions
- Use one of the actions printed in the error message (typically 'update' or 'quick-update').
- For a fresh install, omit --action — it only gates the existing-install menu.
- Ensure an existing BMAD install is present if you want quick-update.
Example fix
# before # --action install # not a valid action # --action quick-update # no existing install present # # after # --action update # valid for existing installs # (fresh install: drop --action entirely)
Defensive patterns
Strategy: validation
Validate before calling
const VALID = new Set(['update', 'quick-update']);
if (options.action && !VALID.has(options.action)) {
console.error(`--action must be one of: ${[...VALID].join(', ')}. Omit for fresh installs.`);
process.exit(1);
} Type guard
function isValidAction(a, hasExistingInstall) {
if (a === 'update') return true;
if (a === 'quick-update') return hasExistingInstall;
return false;
} Try / catch
try {
await ui.run(options);
} catch (e) {
if (/^Invalid action:/.test(e.message)) {
// drop --action and let the menu (or --yes) choose
delete options.action;
await ui.run(options);
} else { throw e; }
} Prevention
- Only pass --action for existing installs; omit it for fresh installs.
- Cross-check the action name against the error message's valid list before retrying.
When it happens
Trigger: Passing --action install or --action foo; passing --action quick-update against a directory with no existing _bmad install (so quick-update isn't offered).
Common situations: Typo in the action name; using quick-update against a fresh directory; version skew between docs and installer; passing an action that only applies to existing installs.
Related errors
- No valid actions available for this installation
- version is not available when nothing is installed
- ${label} does not exist: ${dirPath}
- ${label} is not a directory: ${dirPath}
- ${label} is not readable: ${dirPath}
AI-assisted analysis of bmad-code-org/BMAD-METHOD@b70486b9bd (2026-08-13).
Data as JSON: /api/errors/053804b3477ca398.
Report an issue: GitHub.