phacility/phabricator · error · Exception

Unexpected object type from `%s`: %s

Error message

Unexpected object type from `%s`: %s

What it means

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.

Source

Thrown at src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php:178

      list($identifier, $type) = $parts;

      if ($type == 'missing') {
        // This is either an ambiguous reference which resolves to several
        // objects, or an invalid reference. For now, always treat it as
        // invalid. It would be nice to resolve all possibilities for
        // ambiguous references at some point, although the strategy for doing
        // so isn't clear to me.
        continue;
      }

      switch ($type) {
        case 'commit':
          break;
        case 'tag':
          $tags[] = $identifier;
          break;
        default:
          throw new Exception(
            pht(
              'Unexpected object type from `%s`: %s',
              'git cat-file',
              $line));
      }

      $hits[] = array(
        'ref' => $ref,
        'type' => $type,
        'identifier' => $identifier,
      );
    }

    $tag_map = array();
    if ($tags) {
      // If some of the refs were tags, just load every tag in order to figure
      // out which commits they map to. This might be somewhat inefficient in
      // repositories with a huge number of tags.

View on GitHub (pinned to 5720a38cfe)

Solutions

  1. Normalize identifiers to commits first: git rev-parse <ref>^{commit}
  2. Validate user input against a 40-hex pattern and resolve once to confirm the object type is commit
  3. If you need tree/blob metadata, query cat-file directly instead of routing treeishes through the commit ref resolver

Example fix

// before
$query = id(new DiffusionLowLevelResolveRefsQuery())
  ->setRepository($repository)
  ->withRefs(array($user_hash)); // may be a tree/blob SHA

// after
$future = $repository->getLocalCommandFuture(
  'rev-parse --verify %s^{commit}',
  $user_hash);
list($stdout) = $future->resolvex();
$commit = trim($stdout);
$query = id(new DiffusionLowLevelResolveRefsQuery())
  ->setRepository($repository)
  ->withRefs(array($commit));
Defensive patterns

Strategy: validation

Validate before calling

// Confirm the object is a commit before resolving it as a ref
$future = $repository->getLocalCommandFuture('cat-file -t %s', $identifier);
list($stdout) = $future->resolvex();
if (trim($stdout) !== 'commit' && trim($stdout) !== 'tag') {
  // reject: resolver only handles commits (and tags it can peel)
}

Type guard

function isCommitHash($raw) {
  return (bool)preg_match('/^[0-9a-f]{40}$/i', trim($raw));
}

Try / catch

try {
  $hits = $resolve_query->execute();
} catch (Exception $ex) {
  if (preg_match('/Unexpected object type/', $ex->getMessage())) {
    return new Aphront404Response(); // user supplied a tree/blob identifier
  }
  throw $ex;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21). Data as JSON: /api/errors/11a41aae464a56ca. Report an issue: GitHub.