ruvnet/ruflo · error · Error

policy administration requires an interactive local terminal

Error message

policy administration requires an interactive local terminal

What it means

Thrown by requireInteractiveAdministrator() when either process.stdin.isTTY or process.stdout.isTTY is false. Mutating policy operations (init with a mode, rule add, budget set, revoke) require a human-in-the-loop terminal so approval and audit context is visible; non-interactive contexts (piped input, CI logs, redirected stdout, nohup) are refused to prevent blind automated policy changes.

Source

Thrown at v3/@claude-flow/cli/src/commands/policy.ts:33

  setPolicyMode,
  upsertPolicyRule,
  verifyPolicyLedger,
} from '../services/policy-runtime.js';

function argJson<T>(value: string | undefined, label: string): T {
  if (!value) throw new Error(`${label} requires a JSON argument`);
  try { return JSON.parse(value) as T; }
  catch { throw new Error(`${label} must be valid JSON`); }
}

function print(data: unknown): CommandResult {
  output.writeln(JSON.stringify(data, null, 2));
  return { success: true, exitCode: 0, data };
}

function requireInteractiveAdministrator(): void {
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
    throw new Error('policy administration requires an interactive local terminal');
  }
}

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();

View on GitHub (pinned to 6b01dc5a68)

Solutions

  1. Run the mutating command from a real interactive shell (sit at the terminal, do not pipe or redirect).
  2. For SSH, allocate a TTY: `ssh -t host ruflo policy init --mode enforce`.
  3. For Docker, run with `-it`: `docker run -it ... ruflo policy init --mode enforce`.
  4. For CI, perform policy mutation in a pre-deploy step executed by an operator, not the pipeline itself; the pipeline may run `policy status` (read-only, no TTY needed).

Example fix

// before (CI, no TTY)
ruflo policy init --mode enforce > deploy.log 2>&1
// after (operator runs interactively)
ruflo policy init --mode enforce
Defensive patterns

Strategy: validation

Validate before calling

function assertInteractiveAdmin(): void {
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
    throw new Error('policy administration requires an interactive local terminal');
  }
}
// call before constructing the policy command invocation:
assertInteractiveAdmin();

Type guard

const isInteractiveShell = (): boolean =>
  Boolean(process.stdin.isTTY && process.stdout.isTTY);

Try / catch

try {
  await runPolicyMutation();
} catch (e) {
  const msg = e instanceof Error ? e.message : String(e);
  if (msg.includes('interactive local terminal')) {
    console.error('Re-run from an interactive shell (ssh -t / docker run -it).');
    process.exit(2);
  }
  throw e;
}

Prevention

When it happens

Trigger: Running `ruflo policy init --mode enforce`, `rule add`, `budget set`, or `revoke` inside a CI runner, cron job, ssh -t-less session, with stdin piped from a file, with stdout redirected to a log, or under any process supervisor that detaches the TTY.

Common situations: CI/CD pipelines that try to initialize policy mode during deploy, Docker containers run non-interactively, scripts that pipe a here-doc into the CLI, or running over `ssh host ruflo ...` without `-t`.

Related errors


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