ruvnet/ruflo · error · Error

${label} must be valid JSON

Error message

${label} must be valid JSON

What it means

Thrown by argJson() when JSON.parse() rejects the supplied string. It signals the argument was present but not well-formed JSON, so the policy engine refuses to interpret it rather than guess at a partial structure. Distinct from error 200, which fires on a missing argument.

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 6b01dc5a68)

Solutions

  1. Validate the string with `echo '<json>' | jq .` before running the command — jq reports the exact parse error.
  2. Single-quote the whole JSON blob in the shell so internal double quotes survive: `'{"id":"x"}'`.
  3. Generate the JSON with a tool (jq -c, node -e) and pass its stdout rather than hand-typing.

Example fix

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

Strategy: validation

Validate before calling

function safeJsonParse<T>(s: string | undefined, label: string): T {
  if (!s) throw new Error(`${label} requires a JSON argument`);
  try { return JSON.parse(s) as T; }
  catch { throw new Error(`${label} must be valid JSON`); }
}
// pre-check before the CLI call:
safeJsonParse(process.env.POLICY_JSON, 'policy arg');

Type guard

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

Try / catch

try {
  await runPolicyCommand(argv);
} catch (e) {
  const msg = e instanceof Error ? e.message : String(e);
  if (msg.endsWith('must be valid JSON')) {
    console.error('Argument was not valid JSON. Validate with: echo %s | jq .', arg);
    process.exit(2);
  }
  throw e;
}

Prevention

When it happens

Trigger: Passing an unquoted/incorrectly-quoted object to rule add / budget set / evaluate, e.g. `policy budget set {id:x}` (unquoted braces, bad JSON), trailing commas, single-quoted JSON keys, or a JSON string whose outer quotes the shell consumed.

Common situations: Shell quoting mistakes (double quotes around a JSON body containing double quotes), hand-typing JSON with trailing commas or unquoted keys, or piping a non-JSON value into the positional slot.

Related errors


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