theonedev/onedev · error · ExplicitException
Invalid artifact request path
Error message
Invalid artifact request path
What it means
Thrown by TestArtifactResource.newResourceResponse when the requested artifact path from a unit test report download URL does not start with the required "artifacts/" prefix after being normalized into slash-joined path segments. The resource only serves files stored under the artifacts directory of a test report, so any other path is rejected as invalid to prevent serving arbitrary or malformed paths.
Source
Thrown at server-plugin/server-plugin-report-unittest/src/main/java/io/onedev/server/plugin/report/unittest/TestArtifactResource.java:74
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");
String fileName = StringUtils.substringAfterLast(artifactPath, "/");
ResourceResponse response = new ResourceResponse();
response.getHeaders().addHeader("X-Content-Type-Options", "nosniff");
response.setContentDisposition(ContentDisposition.ATTACHMENT);
try {
response.setContentType(MimeUtils.sanitize(Files.probeContentType(Paths.get(artifactPath))));
} catch (IOException e) {
throw new RuntimeException(e);
}
response.disableCaching();
response.setFileName(URLEncoder.encode(fileName, UTF_8));
response.setWriteCallback(new WriteCallback() {
@Override
public void writeData(Attributes attributes) throws IOException {
UnitTestReport.downloadArtifact(projectId, buildNumber, reportName, artifactPath,
attributes.getResponse().getOutputStream());View on GitHub (pinned to d44925c47c)
Solutions
- Ensure the artifact URL path starts with 'artifacts/' followed by the file path (e.g. .../artifacts/build/log.txt).
- Copy the artifact link from the OneDev UI (test report page) rather than constructing it manually.
- Check for truncated or URL-encoded characters in the path when building links in automation scripts.
- Verify the report actually contains artifacts; if no artifacts were published the link may be malformed.
Example fix
// before (bad link) GET /~downloads/unittest/1/build/log.txt // after GET /~downloads/unittest/1/artifacts/build/log.txt
Defensive patterns
Strategy: validation
Validate before calling
String artifactPath = Joiner.on("/").join(pathSegments);
if (artifactPath == null || !artifactPath.startsWith("artifacts/")) {
// don't call the resource; fix the URL first
} Prevention
- Always build artifact URLs from the paths shown in the OneDev test report UI.
- Keep the 'artifacts/' prefix constant when generating links in CI scripts.
- URL-encode each path segment but keep '/' as separator.
When it happens
Trigger: A GET request to the unit-test artifact resource whose URL path segments, after stripping empty segments and joining with '/', do not begin with 'artifacts/' (e.g. missing the prefix, a mistyped URL, or a manually crafted link pointing outside the artifacts tree).
Common situations: Users hand-editing or copying truncated artifact download links; CI scripts constructing artifact URLs programmatically without the artifacts/ prefix; stale bookmarks after URL scheme changes; attempts to probe other report files via the artifact endpoint.
Understand the failure class
Background: "Invalid URL" / "URL cannot be empty": fix the malformed or missing URL behind request-construction failures — this error's family across 50 libraries.
Related errors
- ${e.getMessage()}
- Reviewer not found:
- Assignee not found:
- Not authenticated
- Ref name is required when commit hash is specified
AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06).
Data as JSON: /api/errors/2f264f23cfb83884.
Report an issue: GitHub.