theonedev/onedev · error · ObjectAlreadyExistsException

Overlapped blob path: " + blobPath

Error message

Overlapped blob path: " + blobPath

What it means

When inserting new blobs in a single commit, DefaultGitService rejects a request where a new file path (e.g. 'file.txt') collides with the top-level segment of another new nested path (e.g. 'file.txt/sub/file') — git cannot hold both a file and a directory with the same name. The overlap is detected by comparing top-level segments of new paths against new top-level files and ObjectAlreadyExistsException is thrown.

Source

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

						Set<String> files = new HashSet<>();
						for (Map.Entry<String, BlobContent> entry : currentNewBlobs.entrySet()) {
							String path = entry.getKey();
							if (!path.contains("/")) {
								files.add(path);
								entries.add(new TreeFormatterEntry(path, entry.getValue().getMode(),
										inserter.insert(Constants.OBJ_BLOB, entry.getValue().getBytes())));
								files.add(path);
							}
						}
						Set<String> topLevelPathSegments = new LinkedHashSet<>();
						for (String path : currentNewBlobs.keySet()) {
							if (path.contains("/")) {
								String topLevelPathSegment = StringUtils.substringBefore(path, "/");
								if (files.contains(topLevelPathSegment)) {
									String blobPath = topLevelPathSegment;
									if (parentPath != null)
										blobPath = parentPath + "/" + path;
									throw new ObjectAlreadyExistsException("Overlapped blob path: " + blobPath);
								} else {
									topLevelPathSegments.add(topLevelPathSegment);
								}
							}
						}
						for (String topLevelPathSegment : topLevelPathSegments) {
							Map<String, BlobContent> childNewBlobs = new HashMap<>();
							for (Map.Entry<String, BlobContent> entry : currentNewBlobs.entrySet()) {
								String path = entry.getKey();
								if (path.startsWith(topLevelPathSegment + "/"))
									childNewBlobs.put(path.substring(topLevelPathSegment.length() + 1), entry.getValue());
							}
							if (parentPath == null)
								parentPath = topLevelPathSegment;
							else
								parentPath += "/" + topLevelPathSegment;
							ObjectId childTreeId = insertTree(revTree, treeWalk, inserter, parentPath,
									Sets.newHashSet(), childNewBlobs);

View on GitHub (pinned to d44925c47c)

Solutions

  1. Rename either conflicting path so no name is used both as a file and as a directory in the same commit.
  2. Split into two commits: first remove the file that must become a directory, then add the nested files.
  3. Validate the new blob path set before committing: no path P and P/… may coexist.

Example fix

// before
edits.addNewBlob("config", ...);
edits.addNewBlob("config/app.yml", ...); // conflict
// after
String file = "config", dir = "config/app.yml";
if (dir.startsWith(file + "/")) throw new IllegalArgumentException(file + " cannot be both file and directory");
Defensive patterns

Strategy: validation

Validate before calling

Set<String> files = newBlobs.keySet().stream().filter(p -> !p.contains("/")).collect(Collectors.toSet());
boolean conflict = newBlobs.keySet().stream().anyMatch(p -> p.contains("/") && files.contains(StringUtils.substringBefore(p, "/")));
if (conflict) throw new IllegalArgumentException("A new path is used both as file and directory");

Try / catch

try { commitBlobs(edits); } catch (ObjectAlreadyExistsException e) { log.error("Overlapping paths in batch: {}", e.getMessage()); }

Prevention

When it happens

Trigger: Committing a batch of new blobs (BlobEdits.newBlobs) via the git service where one new path is a plain file 'X' and another new path starts with 'X/', in the same request.

Common situations: Bulk uploads/imports generating file trees programmatically where one component is both a file and a directory; adding a directory of files under a name that already exists as a file in the same batch; CI-generated configs writing both 'module' and 'module/x' paths.

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/f632229771a9c9f8. Report an issue: GitHub.