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
- 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
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
- 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
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
- Unexpected line count from `%s`!
- Failed to parse `%s` output: %s
- Object "%s" must be a Differential revision.
- Object "%s" specified by "--revision" must be a Differential
- Unexpected number of output lines from "git diff-tree" when
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/11a41aae464a56ca.
Report an issue: GitHub.