{"record":{"id":"47a6bcd5fc883ba0","repo":"phacility/phabricator","slug":"field-changes-must-be-non-empty","errorCode":null,"errorMessage":"Field \"changes\" must be non-empty.","messagePattern":"Field \"changes\" must be non-empty\\.","errorType":"validation","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/differential/conduit/DifferentialCreateDiffConduitAPIMethod.php","lineNumber":59,"sourceCode":"      'lintStatus'                => 'required '.$status_const,\n      'unitStatus'                => 'required '.$status_const,\n      'repositoryPHID'            => 'optional phid',\n\n      'parentRevisionID'          => 'deprecated',\n      'authorPHID'                => 'deprecated',\n      'repositoryUUID'            => 'deprecated',\n    );\n  }\n\n  protected function defineReturnType() {\n    return 'nonempty dict';\n  }\n\n  protected function execute(ConduitAPIRequest $request) {\n    $viewer = $request->getUser();\n    $change_data = $request->getValue('changes');\n    if ($change_data === null) {\n      throw new Exception(pht('Field \"changes\" must be non-empty.'));\n    }\n\n    $changes = array();\n    foreach ($change_data as $dict) {\n      $changes[] = ArcanistDiffChange::newFromDictionary($dict);\n    }\n\n    $diff = DifferentialDiff::newFromRawChanges($viewer, $changes);\n\n    // TODO: Remove repository UUID eventually; for now continue writing\n    // the UUID. Note that we'll overwrite it below if we identify a\n    // repository, and `arc` no longer sends it. This stuff is retained for\n    // backward compatibility.\n\n    $repository_uuid = $request->getValue('repositoryUUID');\n    $repository_phid = $request->getValue('repositoryPHID');\n    if ($repository_phid) {\n      $repository = id(new PhabricatorRepositoryQuery())","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/differential/conduit/DifferentialCreateDiffConduitAPIMethod.php#L41-L77","documentation":"differential.creatediff requires a 'changes' parameter: an array of ArcanistDiffChange dictionaries, each inflated via ArcanistDiffChange::newFromDictionary and passed to DifferentialDiff::newFromRawChanges. The guard tests only for null ($change_data === null), so the exception 'Field \"changes\" must be non-empty.' fires when the key is absent or explicitly null.","triggerScenarios":"Calling differential.creatediff with no 'changes' key in the parameters dict, or with 'changes' => null because local diff parsing produced nothing.","commonSituations":"Custom tooling that mimics the arc payload but skips the changes key when the working copy is clean; a wrapper that assigns null as a default for missing parser output; version drift where an older client sent a differently-named key.","solutions":["Always include 'changes' as an array with at least one change dictionary in the arc format","If your local parser produced zero changes, abort before calling - an empty diff is not worth creating","Compare your payload against a real 'arc diff --conduit' request captured with --conduit-uri debugging","Note the server only rejects null: an empty array passes but creates a useless empty diff, so validate client-side too"],"exampleFix":"// before\n$params = array(\n  'sourceMachine' => 'build01',\n  'sourcePath' => '/srv/build',\n);\n\n// after\n$params = array(\n  'sourceMachine' => 'build01',\n  'sourcePath' => '/srv/build',\n  'changes' => $parser->getChangeDictionaries(), // >= 1 entry\n);","handlingStrategy":"validation","validationCode":"if (!isset($params['changes']) || !is_array($params['changes'])\n    || count($params['changes']) === 0) {\n  throw new InvalidArgumentException(\n    'changes must be a non-empty array of change dictionaries');\n}","typeGuard":null,"tryCatchPattern":"try {\n  $result = $client->callMethodSynchronous('differential.creatediff', $params);\n} catch (ConduitClientException $ex) {\n  if (strpos($ex->getMessage(), 'must be non-empty') !== false) {\n    // client payload bug: fix the changes key, do not retry unchanged\n    throw new RuntimeException('Bad diff payload', 0, $ex);\n  }\n  throw $ex;\n}","preventionTips":["Assert the local parser produced at least one change before submitting","Model payloads on a captured arc request","Treat an empty changes list as an upstream failure, not a submit input"],"tags":["phabricator","conduit","differential","diff","missing-parameter"],"backgroundTag":"missing-required-parameter","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}