phacility/phabricator · error · Exception

Invalid action '%s'!

Error message

Invalid action '%s'!

What it means

Every entry in the custom-policy `rules` JSON must carry an `action` that is exactly `allow` or `deny`. The controller switches on idx($rule, 'action'); a missing action (null) or any other value falls into the default branch and throws before the rule is stored. The check runs while folding the posted data into rule_data.

Source

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

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

        $rule_obj = $rules[$rule_class];

        $value = $rule_obj->getValueForStorage(idx($rule, 'value'));

        $rule_data[] = array(
          'action' => $action,
          'rule' => $rule_class,
          'value' => $value,
        );
      }

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Give every rule object "action": "allow" or "action": "deny" (lowercase, exact).
  2. Remove rules that should neither allow nor deny instead of inventing another action value.
  3. Run the payload through the type guard below before POSTing.

Example fix

// before
[{"rule":"PhabricatorUsersPolicyRule","value":["PHID-USER-xxx"]}]
// after
[{"action":"allow","rule":"PhabricatorUsersPolicyRule","value":["PHID-USER-xxx"]}]
Defensive patterns

Strategy: validation

Validate before calling

$valid_actions = array('allow' => true, 'deny' => true);
foreach ($payload as $rule) {
  if (empty($valid_actions[idx($rule, 'action')])) {
    throw new Exception(pht('Refusing to POST: rule action must be allow or deny.'));
  }
}

Type guard

function is_valid_policy_action($action) {
  return in_array($action, array('allow', 'deny'), true);
}

Prevention

When it happens

Trigger: Posting a rule object without an `action` key, or with a value like "Allow", "grant", true, or null, to the policy edit endpoint.

Common situations: Scripts that assemble rule payloads by hand, client code ported from a different policy dialect, and replayed requests edited in an intercepting proxy.

Related errors


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