theonedev/onedev · error · NotAcceptableException

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

Error message

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

What it means

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

Source

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

		return children;
	}
	
	@Api(order=100, description="Get metadata and content of specified file")
	@Path("/{projectId}/files/{revisionAndFile:.*}")
	@GET
	public FileResponse getFile(
			@PathParam("projectId") Long projectId, 
			@PathParam("revisionAndFile") @NotEmpty @Api(example="some-branch-or-tag/path/to/file") String revisionAndFile) {
		Project project = projectService.load(projectId);
		if (!SecurityUtils.canReadCode(project)) {
			throw new UnauthorizedException();
		}

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

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

		Blob blob = project.getBlob(blobIdent, true);

		FileResponse response = new FileResponse();
		response.path = blobIdent.path;
		response.sha = blob.getBlobId().name();
		response.base64Content = new String(Base64.encodeBase64(blob.getBytes()));
		response.size = blob.getSize();
		response.isPartial = blob.isPartial();
		return response;
	}
	
	@Api(order=110, description="Create, update, or delete specified file. Return hash of resulting commit", 
			example="46c001b04cba0ca41588841f1ca32f50b582ee9b")
	@Path("/{projectId}/files/{branchAndFile:.*}")
	@POST
	public FileEditResponse editFile(

View on GitHub (pinned to d44925c47c)

Solutions

  1. Append the exact file name to the path ('main/src/App.java' instead of 'main/src').
  2. Use getDirectory when you need the folder listing rather than file contents.
  3. Validate the target path resolves to a blob (e.g. via the directory listing) before requesting contents.

Example fix

// before
curl .../projects/42/files/main/src  // directory

// after
curl .../projects/42/files/main/src/App.java
Defensive patterns

Strategy: validation

Validate before calling

// ensure the path has a file segment before requesting contents
if (!revisionAndFile.includes("/") || pathEndsInDirectory(revisionAndFile)) {
  throw new Error("Provide a full file path: revision/path/to/file");
}

Type guard

function isFilePath(p) { return typeof p === "string" && /\.[A-Za-z0-9]+$/.test(p.split('/').pop() || ""); }

Try / catch

try {
  return await getFile(projectId, revisionAndFile);
} catch (e) {
  if (isNotAcceptable(e) && /not a file/.test(e.message)) {
    return await getDirectory(projectId, revisionAndFile); // it is a folder
  }
  throw e;
}

Prevention

When it happens

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

Common situations: Generic download-file scripts pointed at a folder URL; hardcoding paths that later became directories after refactoring; confusion between the file and directory endpoints.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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