theonedev/onedev · error · BlobEditException

Invalid new path: <path>

Error message

Invalid new path: <path>

What it means

The BlobEdits constructor normalizes each key of the newBlobs map via GitUtils.normalizePath. Keys that are empty, absolute, or contain '..' segments normalize to null and cause a BlobEditException("Invalid new path: ..."). This validates the destination paths of created/modified files before any git operation is attempted.

Source

Thrown at server-core/src/main/java/io/onedev/server/git/BlobEdits.java:51

		this(new HashSet<>(), new HashMap<>());
	}
	
	public BlobEdits(Set<String> oldPaths, Map<String, BlobContent> newBlobs) {
		this.oldPaths = new HashSet<>();
		for (String oldPath: oldPaths) {
			String normalizedPath = GitUtils.normalizePath(oldPath);
			if (normalizedPath != null)
				this.oldPaths.add(normalizedPath);
			else
				throw new BlobEditException("Invalid old path: " + oldPath);
		}
		this.newBlobs = new HashMap<>();
		for (Map.Entry<String, BlobContent> entry: newBlobs.entrySet()) { 
			String normalizedPath = GitUtils.normalizePath(entry.getKey());
			if (normalizedPath != null)
				this.newBlobs.put(normalizedPath, entry.getValue());
			else
				throw new BlobEditException("Invalid new path: " + entry.getKey());
		}
	}

	public Set<String> getOldPaths() {
		return oldPaths;
	}

	public Map<String, BlobContent> getNewBlobs() {
		return newBlobs;
	}

	public void applySuggestion(Project project, Mark mark, List<String> suggestion, ObjectId commitId) {
		Map<String, BlobContent> newBlobs = getNewBlobs();
		BlobContent blobContent = newBlobs.get(mark.getPath());
		if (blobContent == null) {
			BlobIdent newBlobIdent = new BlobIdent(commitId.name(), mark.getPath());
			Blob newBlob = project.getBlob(newBlobIdent, false);
			if (newBlob == null || newBlob.getText() == null || newBlob.getLfsPointer() != null)

View on GitHub (pinned to d44925c47c)

Solutions

  1. Use normalized relative paths as newBlobs keys (e.g. "docs/file.md", never "/docs/file.md" or "a/../b.md").
  2. Normalize or reject paths with GitUtils.normalizePath before inserting them into the map.
  3. Catch BlobEditException and report the offending path to the API caller.
  4. Validate file-name inputs at the UI/API boundary before constructing the commit payload.

Example fix

// before
new BlobEdits(Set.of(), Map.of("/new/file.txt", content)); // throws
// after
new BlobEdits(Set.of(), Map.of("new/file.txt", content));
Defensive patterns

Strategy: validation

Validate before calling

Map<String, BlobContent> safe = new LinkedHashMap<>();
for (var e : newBlobs.entrySet()) {
    String n = GitUtils.normalizePath(e.getKey());
    if (n == null) throw new IllegalArgumentException("Invalid new path: " + e.getKey());
    safe.put(n, e.getValue());
}

Type guard

boolean isValidNewPath(String p) {
    return p != null && GitUtils.normalizePath(p) != null;
}

Try / catch

try {
    BlobEdits edits = new BlobEdits(oldPaths, newBlobs);
} catch (BlobEditException e) {
    // return 400-style error naming the bad path
}

Prevention

When it happens

Trigger: new BlobEdits(oldPaths, newBlobs) where a newBlobs map key is an invalid path (empty string, starts with '/', contains '..', or otherwise fails GitUtils.normalizePath).

Common situations: API clients committing files with absolute paths like "/README.md"; crafted requests attempting path traversal; UI forms submitting empty file names for new files; programmatic commits building paths by naive string concatenation.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


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