apache/hadoop · error · HadoopIllegalArgumentException

"concat: source file " + src + " is invalid or empty or…

Error message

"concat: source file " + src + " is invalid or empty or underConstruction"

What it means

Each source must be a finalized, non-empty regular file: verifySrcFiles rejects srcs where isUnderConstruction() or numBlocks() == 0 with HadoopIllegalArgumentException. Concat splices existing block lists — an open file's block list is still mutating, and an empty file contributes nothing while still requiring inode deletion, so both are rejected up front.

Solutions

  1. Close (or recoverLease for crashed writers) every src before concat.
  2. Filter srcs by st.getLen() > 0 using getFileStatus — zero-length files have zero blocks.
  3. Filter out directories and anything that is not a regular file before building the src array.

Example fix

// before
fs.concat(target, dirListing(dir)); // may contain empty or open files

// after
List<Path> srcs = new ArrayList<>();
for (FileStatus st : fs.listStatus(dir)) {
  if (st.isFile() && st.getLen() > 0 && !st.getPath().equals(target)) {
    srcs.add(st.getPath());
  } // skip empty files and directories
}
if (!srcs.isEmpty()) fs.concat(target, srcs.toArray(new Path[0]));
Defensive patterns

Strategy: validation

Validate before calling

List<Path> ok = new ArrayList<>();
for (Path src : srcs) {
  FileStatus st = fs.getFileStatus(src);
  if (st.isFile() && st.getLen() > 0) ok.add(src); // 0 length => 0 blocks
}

Try / catch

catch (HadoopIllegalArgumentException e) {
  if (e.getMessage() != null && e.getMessage().contains("invalid or empty or underConstruction")) {
    recoverLeasesAndFilterEmpty(srcs); // close/recover open files, drop empties, retry concat
  } else { throw e; }
}

Prevention

When it happens

Trigger: Passing a still-open file (writer has not closed / lease not recovered) as a src; passing a zero-length file (created but never written, or truncated); passing a path that is a directory (INodeFile.valueOf would fail adjacent to this check).

Common situations: Rolling-writer jobs where the current output file is accidentally included in the concat list; empty part files produced by failed map/reduce tasks; compaction globs that match marker/zero-byte files.

Related errors


AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22). Data as JSON: /api/errors/cfdef9e77da546bf. Report an issue: GitHub.

Appendix: source

Thrown at hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSDirConcatOp.java:154

      // make sure all the source files are not in snapshot
      if (srcINode.isInLatestSnapshot(iip.getLatestSnapshotId())) {
        throw new SnapshotException("Concat: the source file " + src
            + " is in snapshot");
      }
      // check if the file has other references.
      if (srcINode.isReference() && ((INodeReference.WithCount)
          srcINode.asReference().getReferredINode()).getReferenceCount() > 1) {
        throw new SnapshotException("Concat: the source file " + src
            + " is referred by some other reference in some snapshot.");
      }
      // source file cannot be the same with the target file
      if (srcINode.equals(targetINode)) {
        throw new HadoopIllegalArgumentException("concat: the src file " + src
            + " is the same with the target file " + targetIIP.getPath());
      }
      // source file cannot be under construction or empty
      if(srcINodeFile.isUnderConstruction() || srcINodeFile.numBlocks() == 0) {
        throw new HadoopIllegalArgumentException("concat: source file " + src
            + " is invalid or empty or underConstruction");
      }

      // source file's preferred block size cannot be greater than the target
      // file
      if (srcINodeFile.getPreferredBlockSize() >
          targetINode.getPreferredBlockSize()) {
        throw new HadoopIllegalArgumentException("concat: source file " + src
            + " has preferred block size " + srcINodeFile.getPreferredBlockSize()
            + " which is greater than the target file's preferred block size "
            + targetINode.getPreferredBlockSize());
      }
      if(srcINodeFile.getErasureCodingPolicyID() !=
          targetINode.getErasureCodingPolicyID()) {
        throw new HadoopIllegalArgumentException("Source file " + src
            + " and target file " + targetIIP.getPath()
            + " have different erasure coding policy");
      }

View on GitHub (pinned to 2add963021)