ruvnet/ruflo · error · Error
mode must be legacy, observe, or enforce
Error message
mode must be legacy, observe, or enforce
What it means
Thrown when the policy mode argument is supplied but is not one of the three allowed modes (legacy, observe, enforce) defined by ADR-324. The modes form an ordered safety ramp from no-op (legacy) through logging-only (observe) to blocking (enforce); an unrecognized value is refused to prevent accidentally landing in an unintended posture.
Source
Thrown at v3/@claude-flow/cli/src/commands/policy.ts:55
export const policyCommand: Command = {
name: 'policy',
description: 'Agentic policy engine — evaluate actions, manage rules/approvals, and verify the decision ledger (ADR-324)',
options: [
{ name: 'mode', type: 'string', description: 'Policy mode: legacy | observe | enforce' },
{ name: 'project-root', type: 'string', description: 'Project root containing .claude-flow/policy' },
],
async action(context: CommandContext): Promise<CommandResult> {
const args = (context as { args?: string[] }).args ?? [];
const flags = (context as { flags?: Record<string, unknown> }).flags ?? {};
const root = String(flags.projectRoot ?? process.cwd());
const operation = args[0] ?? 'status';
try {
if (operation === 'init' || operation === 'migrate') {
if (flags.mode || args[1]) requireInteractiveAdministrator();
const migration = await autoMigratePolicyStateIfNeeded(root);
const mode = String(flags.mode ?? args[1] ?? '') as PolicyState['mode'];
if (mode) {
if (!['legacy', 'observe', 'enforce'].includes(mode)) throw new Error('mode must be legacy, observe, or enforce');
await setPolicyMode(mode, root);
}
return print({ ...migration, state: loadPolicyState(root) });
}
if (operation === 'status') {
const state = loadPolicyState(root);
return print({
version: state.version,
mode: state.mode,
migratedFrom: state.migratedFrom,
rules: state.rules.length,
budgets: state.budgets.length,
approvals: state.approvals.length,
receipts: state.receipts.length,
ledger: await verifyPolicyLedger(root),
});
}
if (operation === 'evaluate') {View on GitHub (pinned to 6b01dc5a68)
Solutions
- Use exactly one of: legacy, observe, enforce (lowercase).
- Run `ruflo policy status` to see the current mode and confirm accepted vocabulary.
- Trim and lowercase the value if it comes from a variable: `mode=$(echo "$MODE" | tr '[:upper:]' '[:lower:]' | xargs)`.
Example fix
// before ruflo policy init --mode enforced // after ruflo policy init --mode enforce
Defensive patterns
Strategy: validation
Validate before calling
const POLICY_MODES = ['legacy', 'observe', 'enforce'] as const;
type PolicyMode = typeof POLICY_MODES[number];
function asPolicyMode(v: string): PolicyMode {
if (!POLICY_MODES.includes(v as PolicyMode)) {
throw new Error(`mode must be one of ${POLICY_MODES.join(', ')}`);
}
return v as PolicyMode;
}
asPolicyMode((process.env.POLICY_MODE ?? '').toLowerCase()); Type guard
const isPolicyMode = (v: unknown): v is 'legacy'|'observe'|'enforce' => typeof v === 'string' && ['legacy','observe','enforce'].includes(v);
Try / catch
try {
await setPolicyMode(mode, root);
} catch (e) {
const msg = e instanceof Error ? e.message : String(e);
if (msg.startsWith('mode must be')) {
console.error('Allowed modes: legacy | observe | enforce');
process.exit(2);
}
throw e;
} Prevention
- Lowercase and trim mode values from config before passing.
- Drive mode from a typed enum in your config layer.
- Run `policy status` to confirm the accepted vocabulary.
When it happens
Trigger: Calling `ruflo policy init --mode <bad>` or `policy init <bad>` (positional args[1]) with a typo such as `enforced`, `strict`, `prod`, `read-only`, or an empty-but-truthy token.
Common situations: Typos, guessing mode names from memory, copy-pasting a mode from an older/newer version of the docs, or trailing whitespace/quotes that survive into the comparison.
Related errors
- ${label} requires a JSON argument
- ${label} must be valid JSON
- revoke requires an approval id
- unknown policy operation: ${operation}
- Invalid route entry: ${JSON.stringify(r)}
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/7ccc8136744d319f.
Report an issue: GitHub.