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

  1. Re-verify all sources exist (fs.exists) immediately before concat
  2. Rebuild the source list fresh at call time instead of caching
  3. Prevent concurrent deletion (locking, ownership, or single-writer design)
  4. 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

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


AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06). Data as JSON: /api/errors/465545b9677549da. Report an issue: GitHub.