{"record":{"id":"2e887409c69c9de7","repo":"phacility/phabricator","slug":"unexpected-line-count-from-s","errorCode":null,"errorMessage":"Unexpected line count from `%s`!","messagePattern":"Unexpected line count from `(.+?)`!","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php","lineNumber":141,"sourceCode":"\n        unset($unresolved[$ref]);\n      }\n    }\n\n    // If we resolved everything, we're done.\n    if (!$unresolved) {\n      return $results;\n    }\n\n    // Try to resolve anything else. This stuff either doesn't exist or is\n    // some ref like \"HEAD^^^\".\n    $future = $repository->getLocalCommandFuture('cat-file --batch-check');\n    $future->write(implode(\"\\n\", $unresolved));\n    list($stdout) = $future->resolvex();\n\n    $lines = explode(\"\\n\", rtrim($stdout, \"\\n\"));\n    if (count($lines) !== count($unresolved)) {\n      throw new Exception(\n        pht(\n          'Unexpected line count from `%s`!',\n          'git cat-file'));\n    }\n\n    $hits = array();\n    $tags = array();\n\n    $lines = array_combine($unresolved, $lines);\n    foreach ($lines as $ref => $line) {\n      $parts = explode(' ', $line);\n      if (count($parts) < 2) {\n        throw new Exception(\n          pht(\n            'Failed to parse `%s` output: %s',\n            'git cat-file',\n            $line));\n      }","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php#L123-L159","documentation":"DiffusionLowLevelResolveRefsQuery handles refs that are not plain known refs (like HEAD^^^) by writing them one per line to 'git cat-file --batch-check' stdin and expecting exactly one output line per input ref. If the returned line count differs from the number of unresolved refs, the batch protocol desynchronized and this exception is raised.","triggerScenarios":"Passing ref strings containing embedded newline characters (they split one logical ref across lines or merge two refs into one), or git emitting error text instead of the batch stream. Ref strings usually arrive unvalidated from URLs or Conduit parameters.","commonSituations":"URLs like /diffusion/X/browse/HEAD%0Afake-ref; automation feeding raw user input as commit identifiers; unusual git versions altering batch output.","solutions":["Strip or reject refs containing newline characters before calling DiffusionResolveRefsQuery / DiffusionCachedResolveRefsQuery","Reproduce manually: printf '<ref>\\n' | git cat-file --batch-check inside the local clone","Check the git binary version on repository hosts; upgrade if the batch output is malformed","Log the $unresolved set when the exception fires to identify the offending ref string"],"exampleFix":"// before\n$refs = id(new DiffusionCachedResolveRefsQuery())\n  ->setRepository($repository)\n  ->withRefs(array($user_supplied_ref))\n  ->execute();\n\n// after\nif (preg_match('/[\\r\\n]/', $user_supplied_ref)) {\n  return new Aphront404Response(); // reject newline-carrying refs early\n}\n$refs = id(new DiffusionCachedResolveRefsQuery())\n  ->setRepository($repository)\n  ->withRefs(array($user_supplied_ref))\n  ->execute();","handlingStrategy":"validation","validationCode":"// Reject refs that would desynchronize the cat-file batch stream\nforeach ($refs as $ref) {\n  if (strpos($ref, chr(10)) !== false || strpos($ref, chr(13)) !== false) {\n    throw new Exception('Invalid ref: contains a newline character');\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  $map = id(new DiffusionLowLevelResolveRefsQuery())\n    ->setRepository($repository)\n    ->withRefs($refs)\n    ->execute();\n} catch (Exception $ex) {\n  $map = array(); // treat every submitted ref as unresolvable and continue\n}","preventionTips":["Never pass URL- or user-derived strings to the ref resolver without stripping newlines","Restrict commit inputs to a known grammar (hex SHA, refs, simple suffixes) at the controller boundary","Log the exact ref list when a batch mismatch fires so the offending string is identifiable"],"tags":["git","ref-resolution","input-validation"],"backgroundTag":"git-cat-file-output-mismatch","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}