juicedata/juicefs · error · FileNotFoundException
one of srcs is missing
Error message
one of srcs is missing
What it means
Thrown by concat() when the native jfs_concat returns ENOENT and the destination still exists — meaning the destination is fine but at least one source file is missing. Distinguishes a missing source from a missing destination (which raises the generic error).
Source
Thrown at sdk/java/src/main/java/io/juicefs/JuiceFileSystemImpl.java:1636
int bufsize = 0;
for (int i = 0; i < srcs.length; i++) {
srcbytes[i] = normalizePath(srcs[i]).getBytes("UTF-8");
bufsize += srcbytes[i].length + 1;
}
Pointer buf = Memory.allocate(Runtime.getRuntime(lib), bufsize);
long offset = 0;
for (int i = 0; i < srcs.length; i++) {
buf.put(offset, srcbytes[i], 0, srcbytes[i].length);
buf.putByte(offset + srcbytes[i].length, (byte) 0);
offset += srcbytes[i].length + 1;
}
int r = lib.jfs_concat(Thread.currentThread().getId(), handle, normalizePath(dst), buf, bufsize);
if (r < 0) {
if (r == ENOENT) {
if (!exists(dst)) {
throw error(r, dst);
} else {
throw new FileNotFoundException("one of srcs is missing");
}
}
throw error(r, dst);
}
}
@Override
public boolean rename(Path src, Path dst) throws IOException {
if (needCheckPermission()) {
if (!superGroupFileSystem.exists(src)) {
return false;
}
access(src.getParent(), FsAction.WRITE);
Path dstAncestor = rangerPermissionChecker.getAncestor(dst).getPath();
access(dstAncestor, FsAction.WRITE);
return superGroupFileSystem.rename(src, dst);
}
statistics.incrementWriteOps(1);View on GitHub (pinned to c9a67b23e8)
Solutions
- Re-verify all sources exist (fs.exists) immediately before concat
- Rebuild the source list fresh at call time instead of caching
- Prevent concurrent deletion (locking, ownership, or single-writer design)
- Catch FileNotFoundException and retry with a refreshed source list
Example fix
// before fs.concat(dst, cachedSrcs); // after List<Path> present = new ArrayList<>(); for (Path p : cachedSrcs) if (fs.exists(p)) present.add(p); fs.concat(dst, present.toArray(new Path[0]));
Defensive patterns
Strategy: retry
Validate before calling
for (Path s : srcs) if (!fs.exists(s)) throw new FileNotFoundException(s.toString());
Try / catch
try { fs.concat(dst, srcs); } catch (FileNotFoundException e) { srcs = refreshList(srcs); fs.concat(dst, srcs); } Prevention
- Re-stat sources right before concat
- Avoid concurrent consumers deleting sources
- Rebuild file lists at use time, not upfront
When it happens
Trigger: A source file in srcs was deleted between validation and the native call; a path typo or wrong working directory; concurrent job/gc removed one of the sources; duplicate entries after one was already concatenated and deleted.
Common situations: Concurrent consumers deleting part files while a merger runs; retries after a partial first concat attempt; stale file lists built minutes before the concat executes.
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
- Invalid start or len parameter
- stream was closed
- arguments: " + off + " " + len
- position is negative
- Path already exists: " + f
AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06).
Data as JSON: /api/errors/465545b9677549da.
Report an issue: GitHub.