{"record":{"id":"669be015b70c9046","repo":"phacility/phabricator","slug":"failed-to-decode-rule-data","errorCode":null,"errorMessage":"Failed to decode rule data.","messagePattern":"Failed to decode rule data\\.","errorType":"exception","errorClass":"PhutilProxyException","httpStatus":null,"severity":"error","filePath":"src/applications/herald/controller/HeraldRuleController.php","lineNumber":289,"sourceCode":"    // valid policy value.\n    $repetition_options = $this->getRepetitionOptionMap($adapter);\n    if (!isset($repetition_options[$repetition_policy])) {\n      $repetition_policy = head_key($repetition_options);\n    }\n\n    $e_name = true;\n    $errors = array();\n\n    if (!strlen($new_name)) {\n      $e_name = pht('Required');\n      $errors[] = pht('Rule must have a name.');\n    }\n\n    $data = null;\n    try {\n      $data = phutil_json_decode($request->getStr('rule'));\n    } catch (PhutilJSONParserException $ex) {\n      throw new PhutilProxyException(\n        pht('Failed to decode rule data.'),\n        $ex);\n    }\n\n    if (!is_array($data) ||\n        !$data['conditions'] ||\n        !$data['actions']) {\n      throw new Exception(pht('Failed to decode rule data.'));\n    }\n\n    $conditions = array();\n    foreach ($data['conditions'] as $condition) {\n      if ($condition === null) {\n        // We manage this as a sparse array on the client, so may receive\n        // NULL if conditions have been removed.\n        continue;\n      }\n","sourceCodeStart":271,"sourceCodeEnd":307,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/herald/controller/HeraldRuleController.php#L271-L307","documentation":"Thrown by HeraldRuleController::processRequest when saving a rule: the client POSTs a 'rule' parameter containing the whole rule serialized as JSON, and phutil_json_decode() throws PhutilJSONParserException because the blob is not parseable JSON. The controller wraps it in a PhutilProxyException with this generic message so the underlying parse error (position, reason) travels along with it.","triggerScenarios":"The 'rule' request parameter is truncated, empty, mojibake, or double-encoded when the rule edit form is submitted (Javelin client normally serializes it). Triggered by browser extensions rewriting form bodies, proxies trimming POST data, hand-crafted curl requests with malformed JSON, or client-side bugs that submit before serialization finishes.","commonSituations":"Scripting or fuzzing the /herald/edit/ endpoint without building the Javelin client's exact JSON payload; request-body size limits (post_max_size, proxy limits) silently truncating large rules with many conditions; ad-hoc integrations that send a PHP-style serialized array instead of JSON.","solutions":["Save the rule through the standard rule editor UI and let it serialize the payload; do not hand-edit the hidden 'rule' field.","If scripting the endpoint, build strict JSON (double quotes, no trailing commas) and verify with jq before POSTing.","Raise PHP post_max_size / upload_max_filesize and any proxy body limits if very large rules are being truncated mid-request.","Check the wrapped PhutilJSONParserException message (visible in the exception trace/logs) for the exact parse offset, then fix that spot in the payload."],"exampleFix":"// before — posting a PHP-ish blob\ncurl -d 'rule=conditions=>Array;actions=>Array' .../herald/edit/\n\n// after — post strict JSON matching the client's schema\ncurl -d 'rule={\"name\":\"Triage\",\"conditions\":[[\"title\",\"contains\",\"fix\"]],\"actions\":[[\"addcc\",\"alice\"]],\"logic\":[]}' .../herald/edit/","handlingStrategy":"try-catch","validationCode":"// Server-side pre-flight of the client payload\n$raw = $request->getStr('rule');\ntry {\n  $data = phutil_json_decode($raw);\n} catch (PhutilJSONParserException $ex) {\n  // return a 400 dialog with $ex->getMessage() (shows parse offset)\n}","typeGuard":"function isStrictJson($raw) {\n  if (!is_string($raw) || $raw === '') {\n    return false;\n  }\n  try {\n    phutil_json_decode($raw);\n    return true;\n  } catch (PhutilJSONParserException $ex) {\n    return false;\n  }\n}","tryCatchPattern":"try {\n  $data = phutil_json_decode($request->getStr('rule'));\n} catch (PhutilJSONParserException $ex) {\n  throw new PhutilProxyException(\n    pht('Failed to decode rule data.'),\n    $ex); // preserve the parse position for logs\n}","preventionTips":["Let the standard rule editor serialize the payload; don't hand-craft the hidden field.","Raise post_max_size and proxy body limits so large rules aren't silently truncated.","In integrations, validate the serialized blob with jq before POSTing."],"tags":["herald","phabricator","php","json","http-post","client-payload"],"backgroundTag":"json-parse-error","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}