theonedev/onedev · error · FileNotFoundException
Attachment not found:
Error message
Attachment not found:
What it means
DefaultAttachmentService.getAttachmentInfo reads an attachment file under an attachment group directory for a project. If the file does not exist on the active server's attachment storage, a java.io.FileNotFoundException with message 'Attachment not found: <name>' is thrown. The read is performed under the project's attachment lock to avoid concurrent modification.
Source
Thrown at server-core/src/main/java/io/onedev/server/attachment/DefaultAttachmentService.java:635
} else {
if (!attachmentDir.getParentFile().getName().equals(TEMP))
projectService.directoryModified(projectId, attachmentDir);
return file.getName();
}
});
}
@Override
public String getAttachmentLockName(Long projectId, String attachmentGroup) {
return "attachment:" + projectId + ":" + attachmentGroup;
}
@Override
public FileInfo getAttachmentInfo(Long projectId, String attachmentGroup, String attachment) {
return projectService.runOnActiveServer(projectId, () -> read(getAttachmentLockName(projectId, attachmentGroup), () -> {
File attachmentFile = new File(getAttachmentGroupDir(projectId, attachmentGroup), attachment);
if (!attachmentFile.exists())
throw new FileNotFoundException("Attachment not found: " + attachment);
return new FileInfo(attachment, attachmentFile.lastModified(),
attachmentFile.length(), null);
}));
}
@Override
public void deleteAttachment(Long projectId, String attachmentGroup, String attachment) {
projectService.runOnActiveServer(projectId, () -> write(getAttachmentLockName(projectId, attachmentGroup), () -> {
var attachmentGroupDir = getAttachmentGroupDir(projectId, attachmentGroup);
File attachmentFile = new File(attachmentGroupDir, attachment);
if (attachmentFile.exists()) {
FileUtils.deleteFile(attachmentFile);
projectService.directoryModified(projectId, attachmentGroupDir);
}
return null;
}));
}
View on GitHub (pinned to d44925c47c)
Solutions
- Verify the attachment exists by calling getAttachments(projectId, attachmentGroup) or listAttachmentNames before requesting its info
- Check the exact attachment file name (case-sensitive) against what was passed to uploadAttachment
- If attachments disappeared after a server move, copy the attachments storage directory (project attachments live outside the git repo) to the new server
- Catch java.io.FileNotFoundException around getAttachmentInfo and treat it as a missing-resource condition rather than a crash
Example fix
// before
FileInfo info = attachmentService.getAttachmentInfo(projectId, group, "report.html");
// after
if (!attachmentService.getAttachments(projectId, group).contains("report.html")) {
throw new ExplicitException("Attachment report.html has not been uploaded yet");
}
FileInfo info = attachmentService.getAttachmentInfo(projectId, group, "report.html"); Defensive patterns
Strategy: try-catch
Validate before calling
if (!attachmentService.getAttachments(projectId, attachmentGroup).contains(attachment)) {
throw new ExplicitException("Attachment not uploaded: " + attachment);
} Try / catch
try {
FileInfo info = attachmentService.getAttachmentInfo(projectId, group, attachment);
} catch (FileNotFoundException e) {
// treat as missing resource: skip, re-upload, or report
} Prevention
- List available attachments before accessing one
- Upload attachments before steps that consume them
- Keep attachment storage backed up and copied during server migrations
- Use consistent, programmatic attachment names instead of hand-typed ones
When it happens
Trigger: Calling getAttachmentInfo(projectId, attachmentGroup, attachment) with an attachment name that was never uploaded, was already deleted (e.g. via deleteAttachments or project cleanup), or with a misspelled attachment name. Also occurs when referencing attachments in CI job reports/artifacts that were never produced.
Common situations: CI job step or report referencing an attachment before it is uploaded; attachments lost when moving a project between servers because attachment files live outside git and are not migrated; typos in attachment file names in build spec or UI code; attachment purged by retention cleanup while still referenced.
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
- Allocated agent not connected to current server, please retr
- Malformed build spec
- This build is not authorized to sync to project:
- Loopback address not allowed for target docker image of push
- Step template not found:
AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06).
Data as JSON: /api/errors/94a53afaaa8fd7d7.
Report an issue: GitHub.