theonedev/onedev · error · ObjectAlreadyExistsException

Path already exist: " + treeWalk.getPathString()

Error message

Path already exist: " + treeWalk.getPathString()

What it means

During DefaultGitService tree insertion (used by commit/restore operations), when walking the old tree a path segment matches a new blob name that also exists as a directory conflict: if the existing entry at that position is a tree (TYPE_TREE) while a new blob of the same name is being added, it throws ObjectAlreadyExistsException("Path already exist: <path>") — a blob and a tree cannot occupy the same path.

Source

Thrown at server-core/src/main/java/io/onedev/server/git/service/DefaultGitService.java:704

		Long projectId = project.getId();
		ObjectId commitId = runOnProjectServer(projectId, new ClusterTask<>() {

			private ObjectId insertTree(RevTree revTree, TreeWalk treeWalk, ObjectInserter inserter,
										String parentPath, Set<String> currentOldPaths, Map<String, BlobContent> currentNewBlobs) {
				try {
					List<TreeFormatterEntry> entries = new ArrayList<>();
					while (revTree != null && treeWalk.next()) {
						String name = treeWalk.getNameString();
						if (currentOldPaths.contains(name)) {
							currentOldPaths.remove(name);
							BlobContent currentNewBlob = currentNewBlobs.remove(name);
							if (currentNewBlob != null) {
								ObjectId blobId = inserter.insert(Constants.OBJ_BLOB, currentNewBlob.getBytes());
								entries.add(new TreeFormatterEntry(name, currentNewBlob.getMode(), blobId));
							}
						} else if (currentNewBlobs.containsKey(name)) {
							if ((treeWalk.getRawMode(0) & FileMode.TYPE_MASK) == FileMode.TYPE_TREE) {
								throw new ObjectAlreadyExistsException("Path already exist: " + treeWalk.getPathString());
							} else {
								BlobContent currentNewBlob = currentNewBlobs.remove(name);
								ObjectId blobId = inserter.insert(Constants.OBJ_BLOB, currentNewBlob.getBytes());
								entries.add(new TreeFormatterEntry(name, currentNewBlob.getMode(), blobId));
							}
						} else {
							Set<String> childOldPaths = new HashSet<>();
							for (Iterator<String> it = currentOldPaths.iterator(); it.hasNext(); ) {
								String currentOldPath = it.next();
								if (currentOldPath.startsWith(name + "/")) {
									childOldPaths.add(currentOldPath.substring(name.length() + 1));
									it.remove();
								}
							}
							Map<String, BlobContent> childNewBlobs = new HashMap<>();
							for (Iterator<Map.Entry<String, BlobContent>> it = currentNewBlobs.entrySet().iterator();
								 it.hasNext(); ) {
								Map.Entry<String, BlobContent> entry = it.next();

View on GitHub (pinned to d44925c47c)

Solutions

  1. Rename the new file so it does not collide with the existing directory path
  2. If replacing a directory with a file, perform two commits: first delete all files under the directory, then add the file
  3. Pre-validate in the caller that no new blob path prefixes an existing tree path

Example fix

// before
// adding blob "assets" while tree assets/ exists
fileChange = new FileChange(FileOperation.ADD, "assets", blobId);
// after
fileChange = new FileChange(FileOperation.ADD, "assets/logo.png", blobId); // unique path
Defensive patterns

Strategy: validation

Validate before calling

// caller-side check: no new file path collides with an existing tree
boolean collidesWithDir = existingTreePaths.stream()
    .anyMatch(p -> p.equals(newFilePath)); // p is a known directory entry
if (collidesWithDir)
  throw new IllegalArgumentException("Path collides with existing directory: " + newFilePath);

Try / catch

try {
  gitService.commitTreeChanges(...); // or equivalent tree insertion API
} catch (ObjectAlreadyExistsException e) {
  // e.getMessage() contains the offending path: rename or split commits
}

Prevention

When it happens

Trigger: Calling DefaultGitService methods that add/update files (e.g. commit file changes or create trees) where the change set contains a file whose path collides with an existing directory in the target tree — e.g. adding blob 'docs' when tree 'docs/' exists (or a sibling blob named the same in the same commit set).

Common situations: Adding a file named like an existing folder (case-sensitivity edge cases); API/scripts creating files without checking tree layout; committing a changeset that replaces a directory with a file of the same name in one step.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


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