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

  1. First delete the existing blob at that path (commit removing it), then add the nested files in a follow-up change
  2. Rename the new nested path (or the existing file) to avoid the file-vs-directory conflict
  3. 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

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


AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06). Data as JSON: /api/errors/9c8080dfcd3bd1ed. Report an issue: GitHub.