ruvnet/ruflo · error · Error

must be valid JSON

Error message

${label} must be valid JSON

What it means

policy.ts's argJson() throws when the supplied JSON string reaches the CLI but fails JSON.parse. The argument is present; it is just not strict JSON — quoting damage or syntax errors are the usual cause.

Solutions

  1. Validate the payload first: echo '<payload>' | jq . — jq pinpoints the exact syntax error
  2. On POSIX shells wrap the JSON in single quotes and use double quotes inside it
  3. On Windows, escape inner double quotes or build the JSON in a variable via a tool and pass it through
  4. Remove trailing commas and comments — only strict JSON parses

Example fix

# before
ruflo policy evaluate '{"identity": {"id":"a1",},}'

# after
ruflo policy evaluate '{"identity":{"id":"agent-1","type":"agent"},"action":{"type":"deploy","environment":"production"}}'
Defensive patterns

Strategy: validation

Validate before calling

function mustParse<T>(s: string, label: string): T {
  try { return JSON.parse(s) as T; }
  catch { throw new Error(`${label} is not valid JSON — check quoting/trailing commas`); }
}
const payload = mustParse(rawArg, 'policy payload'); // run before the CLI does

Type guard

function isParsableJson(s: string): boolean {
  try { JSON.parse(s); return true; } catch { return false; }
}

Try / catch

try {
  await runPolicyCli(['rule', 'add', raw]);
} catch (err) {
  if (err instanceof Error && err.message.includes('must be valid JSON')) {
    // re-quote the payload (single-quote outer, double-quote inner) and retry once
  } else throw err;
}

Prevention

When it happens

Trigger: JSON containing single quotes inside a single-quoted shell string, trailing commas, unquoted keys, smart quotes from pasting, or an unquoted JSON string that the shell split into multiple argv entries (so JSON.parse sees only a fragment like '{id:').

Common situations: Windows cmd/PowerShell where single quotes don't group arguments; hand-typing JSON at the prompt; JSON produced by echo with variable interpolation inserting stray characters.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/0b16563932887fa6. Report an issue: GitHub.

Appendix: source

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

  PolicyState,
} from '@claude-flow/security';
import type { Command, CommandContext, CommandResult } from '../types.js';
import { output } from '../output.js';
import {
  autoMigratePolicyStateIfNeeded,
  evaluatePolicyRequest,
  loadPolicyState,
  revokePolicyApproval,
  setPolicyBudget,
  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' },

View on GitHub (pinned to fa13ee4ad6)