theonedev/onedev · error · ExplicitException
Specified markdown path does not exist or is a directory (pr
Error message
Specified markdown path does not exist or is a directory (project: %s, build number: %d, path: %s)
What it means
After passing the traversal checks, the resource resolves the requested markdown file inside <buildDir>/markdown-reports/<reportName>/ and throws ExplicitException if the path does not exist or is a directory. The message includes the project path, build number, and the requested path so you can see exactly what was looked up. It means the build did not publish that file (or the URL points at a directory instead of a file).
Source
Thrown at server-plugin/server-plugin-report-markdown/src/main/java/io/onedev/server/plugin/report/markdown/MarkdownReportDownloadResource.java:84
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 markdownPath = Joiner.on("/").join(pathSegments);
File buildDir = build.getDir();
File reportDir = new File(buildDir, PublishMarkdownReportStep.CATEGORY + "/" + reportName);
File markdownFile = new File(reportDir, markdownPath);
if (!markdownFile.exists() || markdownFile.isDirectory()) {
String message = String.format("Specified markdown path does not exist or is a directory (project: %s, build number: %d, path: %s)",
project.getPath(), build.getNumber(), markdownPath);
throw new ExplicitException(message);
}
ResourceResponse response = new ResourceResponse();
response.getHeaders().addHeader("X-Content-Type-Options", "nosniff");
response.setContentType(MimeTypes.OCTET_STREAM);
response.disableCaching();
try {
response.setFileName(URLEncoder.encode(markdownFile.getName(), StandardCharsets.UTF_8.name()));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
response.setWriteCallback(new WriteCallback() {
@Override
public void writeData(Attributes attributes) throws IOException {
LockUtils.read(PublishMarkdownReportStep.getReportLockName(build), new Callable<Void>() {View on GitHub (pinned to d44925c47c)
Solutions
- Check the build log for the Publish Markdown Report step to confirm which files were published.
- Verify the URL path matches the published file exactly (case-sensitive on Linux agents).
- Re-run the build so the report is published, then retry the download.
- Confirm the file exists under the build's artifact directory rather than a subdirectory.
Example fix
// before String url = ".../markdown-reports/docs/subfolder"; // a directory // after String url = ".../markdown-reports/docs/subfolder/index.md"; // a file
Defensive patterns
Strategy: try-catch
Try / catch
try {
downloadReportFile(buildId, reportName, markdownPath);
} catch (e) {
if (/does not exist or is a directory/.test(e.message)) {
console.error('Check the Publish Markdown Report step output for build', buildId);
} else {
throw e;
}
} Prevention
- Verify the publish step succeeded and note the exact published paths.
- Match file path casing exactly (agents are case-sensitive).
- Point URLs at files, not directories.
- Re-run the build if artifacts were cleaned.
When it happens
Trigger: Requesting a markdown report file that was never published by the build step, a misspelled path, or a path pointing at a subdirectory instead of a .md file.
Common situations: Build step ran in a different build/agent and published files elsewhere; the report name or file path changed in the CI config; links to an old build whose artifacts were cleaned; requesting the report directory itself rather than an index file.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06).
Data as JSON: /api/errors/2970ddf3ee92d5c4.
Report an issue: GitHub.