{"record":{"id":"11a41aae464a56ca","repo":"phacility/phabricator","slug":"unexpected-object-type-from-s-s","errorCode":null,"errorMessage":"Unexpected object type from `%s`: %s","messagePattern":"Unexpected object type from `(.+?)`: (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php","lineNumber":178,"sourceCode":"      list($identifier, $type) = $parts;\n\n      if ($type == 'missing') {\n        // This is either an ambiguous reference which resolves to several\n        // objects, or an invalid reference. For now, always treat it as\n        // invalid. It would be nice to resolve all possibilities for\n        // ambiguous references at some point, although the strategy for doing\n        // so isn't clear to me.\n        continue;\n      }\n\n      switch ($type) {\n        case 'commit':\n          break;\n        case 'tag':\n          $tags[] = $identifier;\n          break;\n        default:\n          throw new Exception(\n            pht(\n              'Unexpected object type from `%s`: %s',\n              'git cat-file',\n              $line));\n      }\n\n      $hits[] = array(\n        'ref' => $ref,\n        'type' => $type,\n        'identifier' => $identifier,\n      );\n    }\n\n    $tag_map = array();\n    if ($tags) {\n      // If some of the refs were tags, just load every tag in order to figure\n      // out which commits they map to. This might be somewhat inefficient in\n      // repositories with a huge number of tags.","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php#L160-L196","documentation":"After parsing each batch line, the ref resolver accepts only object types 'commit', 'tag' (queued for peeling), and 'missing' (treated as invalid). Any other type — in practice 'tree' or 'blob' — hits the default case and throws, because callers of this query expect commits.","triggerScenarios":"Resolving an identifier that names a non-commit object: a raw tree or blob SHA (the 40-hex of a directory or file object), or a treeish expression like master^{tree} or HEAD:some/file.php.","commonSituations":"User-supplied commit parameters in URLs or Conduit that are actually file/tree hashes; tooling that copies object hashes from git ls-tree output instead of commit hashes.","solutions":["Normalize identifiers to commits first: git rev-parse <ref>^{commit}","Validate user input against a 40-hex pattern and resolve once to confirm the object type is commit","If you need tree/blob metadata, query cat-file directly instead of routing treeishes through the commit ref resolver"],"exampleFix":"// before\n$query = id(new DiffusionLowLevelResolveRefsQuery())\n  ->setRepository($repository)\n  ->withRefs(array($user_hash)); // may be a tree/blob SHA\n\n// after\n$future = $repository->getLocalCommandFuture(\n  'rev-parse --verify %s^{commit}',\n  $user_hash);\nlist($stdout) = $future->resolvex();\n$commit = trim($stdout);\n$query = id(new DiffusionLowLevelResolveRefsQuery())\n  ->setRepository($repository)\n  ->withRefs(array($commit));","handlingStrategy":"validation","validationCode":"// Confirm the object is a commit before resolving it as a ref\n$future = $repository->getLocalCommandFuture('cat-file -t %s', $identifier);\nlist($stdout) = $future->resolvex();\nif (trim($stdout) !== 'commit' && trim($stdout) !== 'tag') {\n  // reject: resolver only handles commits (and tags it can peel)\n}","typeGuard":"function isCommitHash($raw) {\n  return (bool)preg_match('/^[0-9a-f]{40}$/i', trim($raw));\n}","tryCatchPattern":"try {\n  $hits = $resolve_query->execute();\n} catch (Exception $ex) {\n  if (preg_match('/Unexpected object type/', $ex->getMessage())) {\n    return new Aphront404Response(); // user supplied a tree/blob identifier\n  }\n  throw $ex;\n}","preventionTips":["Normalize user input with rev-parse <ref>^{commit} before passing it as a commit identifier","Accept only 40-hex commit SHAs or well-formed ref expressions at input boundaries","Do not feed ls-tree output hashes into APIs that expect commits"],"tags":["git","ref-resolution","object-type"],"backgroundTag":"git-ref-wrong-object-type","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}