theonedev/onedev · error · NotTreeException
Path does not represent a tree: " + treeWalk.getPathString()
Error message
Path does not represent a tree: " + treeWalk.getPathString()
What it means
While inserting trees in DefaultGitService, the code expects the old-tree entry at a needed path segment to be a directory so it can recurse (insertTree). If the entry exists but is not a tree (e.g. a blob), it throws NotTreeException("Path does not represent a tree: <path>") — a new blob is being placed under a path that currently exists as a file, so the subtree cannot be created.
Source
Thrown at server-core/src/main/java/io/onedev/server/git/service/DefaultGitService.java:739
it.hasNext(); ) {
Map.Entry<String, BlobContent> entry = it.next();
if (entry.getKey().startsWith(name + "/")) {
childNewBlobs.put(entry.getKey().substring(name.length() + 1), entry.getValue());
it.remove();
}
}
if (!childOldPaths.isEmpty() || !childNewBlobs.isEmpty()) {
if ((treeWalk.getFileMode(0).getBits() & FileMode.TYPE_TREE) != 0) {
TreeWalk childTreeWalk = TreeWalk.forPath(treeWalk.getObjectReader(), treeWalk.getPathString(),
revTree);
Preconditions.checkNotNull(childTreeWalk);
childTreeWalk.enterSubtree();
ObjectId childTreeId = insertTree(revTree, childTreeWalk, inserter, treeWalk.getPathString(),
childOldPaths, childNewBlobs);
if (childTreeId != null)
entries.add(new TreeFormatterEntry(name, FileMode.TREE.getBits(), childTreeId));
} else {
throw new NotTreeException("Path does not represent a tree: " + treeWalk.getPathString());
}
} else {
entries.add(new TreeFormatterEntry(name, treeWalk.getFileMode(0).getBits(), treeWalk.getObjectId(0)));
}
}
}
if (!currentOldPaths.isEmpty()) {
String nonExistPath = currentOldPaths.iterator().next();
if (parentPath != null)
nonExistPath = parentPath + "/" + nonExistPath;
throw new NotFoundException("Unable to find path " + nonExistPath);
}
if (!currentNewBlobs.isEmpty()) {
Set<String> files = new HashSet<>();
for (Map.Entry<String, BlobContent> entry : currentNewBlobs.entrySet()) {
String path = entry.getKey();View on GitHub (pinned to d44925c47c)
Solutions
- First delete the existing blob at that path (commit removing it), then add the nested files in a follow-up change
- Rename the new nested path (or the existing file) to avoid the file-vs-directory conflict
- Pre-check the target tree: resolve the parent path and verify it is a tree before applying changes
Example fix
// before // existing blob "docs", adding "docs/index.md" changes.add(new FileChange(FileOperation.ADD, "docs/index.md", mdBlobId)); // after changes.add(new FileChange(FileOperation.DELETE, "docs", oldBlobId)); changes.add(new FileChange(FileOperation.ADD, "docs/index.md", mdBlobId));
Defensive patterns
Strategy: validation
Validate before calling
// resolve parent path in the old tree; must be a tree to add children
ObjectId parent = TreeWalk.forPath(repo, parentPath, revTree) != null
? /* check its FileMode */ : null;
if (parent != null && !isTree(parent))
throw new IllegalArgumentException("Existing file blocks nested path: " + parentPath); Type guard
boolean isTreeEntry(FileMode mode) {
return (mode.getBits() & FileMode.TYPE_MASK) == FileMode.TYPE_TREE;
} Try / catch
try {
gitService.addFilesToTree(...);
} catch (NotTreeException e) {
// path in message exists as a blob: delete it first or choose another path
} Prevention
- Verify parent segments of any new nested path are directories in the target tree
- Delete conflicting flat files before adding directory hierarchies
- Beware case-only collisions when clients run on case-insensitive filesystems
When it happens
Trigger: Committing/adding a file like 'assets/logo.png' when the existing tree already contains a blob named 'assets' (a file, not a directory); the tree walk hits the blob where a subtree should be entered and insertTree's child-walk is impossible.
Common situations: Migrations/imports that add nested paths over a flat file layout; restoring snapshots with changed file/folder roles; case-insensitive filesystems causing a file 'Assets' to collide with dir 'assets/'.
Related errors
- Path already exist: " + treeWalk.getPathString()
- Ref name is required when commit hash is specified
- Either commit hash, branch or tag should be specified
- Unable to find commit to import build spec (import project:
- No default branch in project:
AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06).
Data as JSON: /api/errors/9c8080dfcd3bd1ed.
Report an issue: GitHub.