theonedev/onedev · error · UnauthorizedException

This api can only be accessed via cluster credential

Error message

This api can only be accessed via cluster credential

What it means

ClusterResource.downloadSiteFiles is an internal cluster-only API that streams files from the server's site directory matching a pattern set. It rejects any caller not authenticated as the system principal via the cluster credential, throwing UnauthorizedException "This api can only be accessed via cluster credential".

Source

Thrown at server-core/src/main/java/io/onedev/server/cluster/ClusterResource.java:102

	private PackBlobService packBlobService;
	
	@Inject
	private BuildService buildService;
	
	@Inject
	private RunCacheService cacheService;
	
	@Inject
	private WorkExecutionService workExecutionService;
	
	@Path("/site-files")
	@Produces(MediaType.APPLICATION_OCTET_STREAM)
	@GET
	public Response downloadSiteFiles(@QueryParam("path") String path,
								  @QueryParam("patterns") String patterns,
								  @QueryParam("readLock") String readLock) {
		if (!SecurityUtils.isSystem())
			throw new UnauthorizedException("This api can only be accessed via cluster credential");

		StreamingOutput output = os -> {
			Runnable sendFiles = () -> {
				File directory = new File(Bootstrap.getSiteDir(), path);
				PatternSet patternSet = PatternSet.parse(patterns);
				patternSet.getExcludes().add(SHARE_TEST_DIR + "/**");
				TarUtils.tar(directory, patternSet.getIncludes(), patternSet.getExcludes(), os, false);
			};
			if (readLock != null)
				read(readLock, sendFiles);
			else
				sendFiles.run();
		};
		return ok(output).build();
	}
	
	@Path("/site-file")
	@Produces(MediaType.APPLICATION_OCTET_STREAM)

View on GitHub (pinned to d44925c47c)

Solutions

  1. Use the cluster credential (server secret) authentication for this endpoint — it is meant for inter-server cluster communication only.
  2. Verify both member servers share the same cluster secret/token so SecurityUtils.isSystem() evaluates true.
  3. Do not call this endpoint from user code; use the OneDev UI or public APIs to access site files.
  4. Check reverse proxy / auth middleware is not stripping the cluster auth header before it reaches OneDev.

Example fix

// before
curl -H "Authorization: Bearer <personal-access-token>" \
  https://onedev.example.com/api/cluster/site-files?path=cache

// after (cluster member using cluster credential setup, not a PAT)
// ensure the request originates from an authenticated cluster member;
// configure identical cluster secrets on both servers, then call via the
// cluster client rather than raw HTTP with user tokens.
Defensive patterns

Strategy: try-catch

Validate before calling

if (!SecurityUtils.isSystem())
    throw new IllegalStateException("Cluster endpoint requires cluster credential, not user auth");

Try / catch

try {
    return target("cluster/site-files").get(byte[].class);
} catch (ForbiddenException | UnauthorizedException e) {
    // not called with cluster credential; fix client authentication
}

Prevention

When it happens

Trigger: Calling GET on the site-files endpoint with normal user credentials, a regular access token, or no credentials instead of the server's cluster secret; pointing a third-party tool at this internal endpoint.

Common situations: Manually curling the endpoint for debugging without cluster auth; a member server whose cluster token is missing/mismatched fails the system check; firewall-routed requests stripping the credential header; API exploration with a regular PAT.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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