theonedev/onedev · error · EntityNotFoundException

Unable to find build (project: %s, build number: %d)

Error message

Unable to find build (project: %s, build number: %d)

What it means

For non-system requests, the resource loads the project, then looks up the build via BuildService.find(project, buildNumber). If no build with that number exists in the project it throws EntityNotFoundException("Unable to find build (project: %s, build number: %d)"). This validates that the artifact request targets a real build before any permission checks or file access.

Source

Thrown at server-plugin/server-plugin-report-unittest/src/main/java/io/onedev/server/plugin/report/unittest/TestArtifactResource.java:56

	private static final String PARAM_BUILD = "build";

	private static final String PARAM_REPORT = "report";

	@Override
	protected ResourceResponse newResourceResponse(Attributes attributes) {
		var params = attributes.getParameters();
		var projectId = params.get(PARAM_PROJECT).toLong();
		var buildNumber = params.get(PARAM_BUILD).toLong();
		String reportName = params.get(PARAM_REPORT).toString();
		if (reportName.contains(".."))
			throw new ExplicitException("Invalid request path");

		if (!SecurityUtils.isSystem()) {
			var project = OneDev.getInstance(ProjectService.class).load(projectId);
			var build = OneDev.getInstance(BuildService.class).find(project, buildNumber);
			if (build == null) {
				throw new EntityNotFoundException(String.format(
						"Unable to find build (project: %s, build number: %d)",
						project.getPath(), buildNumber));
			}
			if (!SecurityUtils.canAccessReport(build, reportName))
				throw new UnauthorizedException();
		}

		List<String> pathSegments = new ArrayList<>();
		for (int i = 0; i < params.getIndexedCount(); i++) {
			String pathSegment = params.get(i).toString();
			if (pathSegment.contains(".."))
				throw new ExplicitException("Invalid request path");
			if (pathSegment.length() != 0)
				pathSegments.add(pathSegment);
		}
		String artifactPath = Joiner.on("/").join(pathSegments);
		if (!artifactPath.startsWith(ARTIFACTS + "/"))
			throw new ExplicitException("Invalid artifact request path");

View on GitHub (pinned to d44925c47c)

Solutions

  1. Verify the project id and build number pair via the project's build list or REST API.
  2. Confirm the build wasn't deleted by retention/cleanup rules.
  3. Update the script/config to resolve the latest build dynamically instead of hard-coding numbers.
  4. Ensure you are querying the project that actually owns the build.

Example fix

// before
long buildNumber = 42; // hard-coded, build deleted

// after
Build build = oneDev.buildQueryManager().find(project, "$ latest");
long buildNumber = build.getNumber();
Defensive patterns

Strategy: try-catch

Validate before calling

# resolve latest build before requesting artifacts
const builds = await api.get(`/projects/${projectId}/builds?offset=0&count=1`);
const buildNumber = builds[0].number;

Try / catch

try {
  downloadTestArtifact(projectId, buildNumber, reportName, artifactPath);
} catch (e) {
  if (/Unable to find build/.test(e.message)) {
    console.error(`Build #${buildNumber} not found in project ${projectId}; refresh build reference`);
  } else {
    throw e;
  }
}

Prevention

When it happens

Trigger: Calling the test artifact resource with a build number that does not exist in the given project — wrong project id paired with a valid build number, a deleted build, or a build from another project.

Common situations: Copy-pasted URLs across projects; builds removed by cleanup/retention policies; script caching an old build id; off-by-one in an automation loop enumerating build numbers.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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