theonedev/onedev · error · NotAcceptableException

Specified path is not a directory: ${blobIdent.path}

Error message

Specified path is not a directory: ${blobIdent.path}

What it means

RepositoryResource.getDirectory parses the 'revisionAndDirectory' path parameter into a BlobIdent and requires it to designate a tree. If the path actually points to a blob (file) rather than a directory, it throws a NotAcceptableException with message "Specified path is not a directory: <path>".

Source

Thrown at server-core/src/main/java/io/onedev/server/rest/resource/RepositoryResource.java:325

		return EnumSet.allOf(LogCommand.Field.class).stream().map(Enum::name).collect(toList());
	}
	
	@Api(order=90, description="Get children of specified directory")
	@Path("/{projectId}/directories/{revisionAndDirectory:.*}")
	@GET
	public List<DirectoryChild> getDirectory(
			@PathParam("projectId") Long projectId, 
			@PathParam("revisionAndDirectory") @NotEmpty @Api(example="some-branch-or-tag/path/to/directory") String revisionAndDirectory) {
		Project project = projectService.load(projectId);
		if (!SecurityUtils.canReadCode(project)) {
			throw new UnauthorizedException();
		}

		List<String> revisionAndPathSegments = Splitter.on('/').splitToList(revisionAndDirectory);
		BlobIdent blobIdent = new BlobIdent(project, revisionAndPathSegments);

		if (!blobIdent.isTree()) {
			throw new NotAcceptableException("Specified path is not a directory: " + blobIdent.path);
		}

		ObjectId revId = project.getObjectId(blobIdent.revision, true);
		
		List<DirectoryChild> children = new ArrayList<>();
		for (BlobIdent childIdent: gitService.getChildren(
				project, revId, blobIdent.path, BlobIdentFilter.ALL, false)) {
			DirectoryChild child = new DirectoryChild();
			child.path = childIdent.path;
			child.isFile = (FileMode.TYPE_MASK & childIdent.mode) == FileMode.TYPE_FILE;
			children.add(child);
		}
		
		return children;
	}
	
	@Api(order=100, description="Get metadata and content of specified file")
	@Path("/{projectId}/files/{revisionAndFile:.*}")

View on GitHub (pinned to d44925c47c)

Solutions

  1. Point the path at a directory — drop any trailing file segment (request 'main/src' not 'main/src/App.java').
  2. If you need file contents, call the getFile endpoint instead of getDirectory.
  3. When walking listings programmatically, branch on the entry type returned by DirectoryChild (file vs directory) before recursing.

Example fix

// before
curl .../projects/42/files/main/README.md   // file path

// after
curl .../projects/42/files/main             // directory path
Defensive patterns

Strategy: validation

Validate before calling

// when walking listings, only recurse into directory children
if (child.type === "directory") {
  await getDirectory(projectId, `${revision}/${child.path}`);
}

Type guard

function isDirectoryPath(p) { return !path.extname(p) || p.endsWith("/"); } // heuristic; confirm via listing

Try / catch

try {
  return await getDirectory(projectId, revAndDir);
} catch (e) {
  if (isNotAcceptable(e) && /not a directory/.test(e.message)) {
    return await getFile(projectId, revAndDir); // fallback to file read
  }
  throw e;
}

Prevention

When it happens

Trigger: GET .../files/{revisionAndDirectory} where the path portion of revisionAndDirectory names a file (e.g. 'main/src/App.java') instead of a directory ('main/src').

Common situations: Clients walking a tree that mistakenly pass file entries returned by a previous listing; trailing-path bugs that drop or misplace the directory segment; confusing a symlink/file with a folder.

Related errors


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