{"record":{"id":"64bfe455c3e5bf0b","repo":"phacility/phabricator","slug":"unexpected-number-of-output-lines-from-git-diff-t","errorCode":null,"errorMessage":"Unexpected number of output lines from \"git diff-tree\" when processing commit (\"%s\"): expected an even number of lines.","messagePattern":"Unexpected number of output lines from \"git diff-tree\" when processing commit \\(\"(.+?)\"\\): expected an even number of lines\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/diffusion/query/lowlevel/DiffusionLowLevelFilesizeQuery.php","lineNumber":49,"sourceCode":"    $repository = $this->getRepository();\n    $identifier = $this->identifier;\n\n    $paths_future = $repository->getLocalCommandFuture(\n      'diff-tree -z -r --no-commit-id %s --',\n      gitsprintf('%s', $identifier));\n\n    // With \"-z\" we get \"<fields>\\0<filename>\\0\" for each line. Process the\n    // delimited text as \"<fields>, <filename>\" pairs.\n\n    $path_lines = id(new LinesOfALargeExecFuture($paths_future))\n      ->setDelimiter(\"\\0\");\n\n    $paths = array();\n\n    $path_pairs = new PhutilChunkedIterator($path_lines, 2);\n    foreach ($path_pairs as $path_pair) {\n      if (count($path_pair) != 2) {\n        throw new Exception(\n          pht(\n            'Unexpected number of output lines from \"git diff-tree\" when '.\n            'processing commit (\"%s\"): expected an even number of lines.',\n            $identifier));\n      }\n\n      list($fields, $pathname) = array_values($path_pair);\n      $fields = explode(' ', $fields);\n\n      // Fields are:\n      //\n      //    :100644 100644 aaaa bbbb M\n      //\n      // [0] Old file mode.\n      // [1] New file mode.\n      // [2] Old object hash.\n      // [3] New object hash.\n      // [4] Change mode.","sourceCodeStart":31,"sourceCodeEnd":67,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/diffusion/query/lowlevel/DiffusionLowLevelFilesizeQuery.php#L31-L67","documentation":"DiffusionLowLevelFilesizeQuery runs 'git diff-tree -z' and parses the NUL-delimited output as (fields, pathname) pairs using PhutilChunkedIterator over LinesOfALargeExecFuture. An odd chunk count means the output violated that contract (record count not divisible by two), so parsing aborts rather than pair a filename with the wrong metadata line. Usually caused by a git version whose diff-tree output differs from what this Phabricator release expects, or an exotic tree entry.","triggerScenarios":"Requesting a file's size for a commit whose 'git diff-tree -z' output yields an odd number of NUL-delimited records: new git output formats, very old git, unusual file modes or submodule entries, or a truncated read from the large-file iterator.","commonSituations":"The system git was upgraded beyond the versions the Phabricator release was tested with (common on long-lived hosts); pinned ancient git versions; repositories with unusual entries.","solutions":["Run the underlying command by hand on the failing commit: 'git diff-tree -z -t <commit>' and inspect whether record pairs line up","Align versions: upgrade Phabricator to a release tested with your git, or pin git to a version your Phabricator supports","If the output looks well-formed, report an upstream bug including the commit identifier and git --version"],"exampleFix":"// before: trust the pair iterator blindly\nforeach (new PhutilChunkedIterator($path_lines, 2) as $pair) {\n  list($fields, $pathname) = array_values($pair);\n}\n\n// after: validate record parity before consuming\n$records = array();\nforeach ($path_lines as $line) { $records[] = $line; }\nif (count($records) % 2 !== 0) {\n  throw new Exception(pht('git diff-tree output parity mismatch for %s.', $identifier));\n}","handlingStrategy":"fallback","validationCode":"// pre-validate git output parity before consuming pairs\n$records = array();\nforeach (new LinesOfALargeExecFuture($paths_future) as $line) { $records[] = $line; }\nif (count($records) % 2 !== 0) {\n  // log git --version and commit, then fall back to a slower path-based query\n}","typeGuard":"function isPairableDiffTreeOutput(array $records) {\n  return count($records) % 2 === 0;\n}","tryCatchPattern":"try {\n  $size = id(new DiffusionLowLevelFilesizeQuery())\n    ->setRepository($repository)\n    ->withCommit($identifier)\n    ->execute();\n} catch (Exception $ex) {\n  // fall back to DiffusionLowLevelPathQuery / changeset-based lookup and report the parse mismatch\n}","preventionTips":["Pin the host git version to one your Phabricator release was tested with","Run the file-size/browse smoke tests against exotic repositories after upgrading git","Capture 'git diff-tree -z' output when the error fires; the raw output is the only way to diagnose version drift"],"tags":["git","parsing","diff-tree","version-compat","phabricator"],"backgroundTag":"unexpected-command-output","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}