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

  1. Use exactly one of: legacy, observe, enforce (lowercase).
  2. Run `ruflo policy status` to see the current mode and confirm accepted vocabulary.
  3. 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

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


AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12). Data as JSON: /api/errors/7ccc8136744d319f. Report an issue: GitHub.