phacility/phabricator · error · PhutilProxyException

Failed to JSON decode rule data!

Error message

Failed to JSON decode rule data!

What it means

Thrown by PhabricatorPolicyEditController while saving a custom policy from the policy edit dialog. The browser submits the whole rule list as a JSON string in the `rules` form field; when phutil_json_decode cannot parse it, the PhutilJSONParserException is rethrown wrapped in a PhutilProxyException with this message. It means the submitted payload is syntactically broken, before any rule semantics are examined.

Source

Thrown at src/applications/policy/controller/PhabricatorPolicyEditController.php:94

      $policy = head($policies);
    } else {
      $policy = id(new PhabricatorPolicy())
        ->setRules(array($default_rule))
        ->setDefaultAction(PhabricatorPolicy::ACTION_DENY);
    }

    $root_id = celerity_generate_unique_node_id();

    $default_action = $policy->getDefaultAction();
    $rule_data = $policy->getRules();

    $errors = array();
    if ($request->isFormPost()) {
      $data = $request->getStr('rules');
      try {
        $data = phutil_json_decode($data);
      } catch (PhutilJSONParserException $ex) {
        throw new PhutilProxyException(
          pht('Failed to JSON decode rule data!'),
          $ex);
      }

      $rule_data = array();
      foreach ($data as $rule) {
        $action = idx($rule, 'action');
        switch ($action) {
          case 'allow':
          case 'deny':
            break;
          default:
            throw new Exception(pht("Invalid action '%s'!", $action));
        }

        $rule_class = idx($rule, 'rule');
        if (empty($rules[$rule_class])) {
          throw new Exception(pht("Invalid rule class '%s'!", $rule_class));

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Reproduce the save once in a normal browser session; if it works there, the failing client is sending malformed JSON.
  2. When scripting the endpoint, build the rules array in code and send json_encode($rules) as the field so it is guaranteed valid JSON.
  3. After an upgrade, clear the celerity cache and hard-refresh if the dialog itself submits garbage.

Example fix

// before: hand-typed field, trailing comma breaks JSON
rules=[{"action":"allow",}]
// after: JSON array of rule objects, string-encoded
rules=[{"action":"allow","rule":"PhabricatorProjectsPolicyRule","value":["PHID-PROJ-xxx"]}]
Defensive patterns

Strategy: try-catch

Validate before calling

// Build the field in code so it is always valid JSON.
$rules = array(
  array('action' => 'allow',
        'rule' => 'PhabricatorProjectsPolicyRule',
        'value' => array($project_phid)),
);
$post['rules'] = json_encode($rules);

Try / catch

Wrap phutil_json_decode() in try { } catch (PhutilJSONParserException $ex) — the parser exception carries the failing byte offset. When rethrowing from a higher layer, wrap it as the controller does: throw new PhutilProxyException(pht('Failed to JSON decode rule data!'), $ex); so the low-level cause stays attached.

Prevention

When it happens

Trigger: POSTing to the policy edit endpoint with a `rules` field that is not valid JSON: a hand-built curl payload, a request mangled by a proxy or browser extension, or stale celerity JS after an upgrade serializing the dialog incorrectly.

Common situations: Automating policy creation against the web endpoint instead of the UI, replaying and editing captured form posts, and half-upgraded installs serving a cached dialog bundle.

Related errors


AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21). Data as JSON: /api/errors/d4f40630c1ae8a7a. Report an issue: GitHub.