GoogleContainerTools/jib · error · IllegalStateException

Cannot enable reproducible timestamps. They can only be enab

Error message

Cannot enable reproducible timestamps. They can only be enabled when the target root doesn't exist or is an empty directory

What it means

ZipUtil.unzip refuses to extract into a destination when enableReproducibleTimestamps is true and the destination is an existing non-empty directory, because reproducible extraction requires a clean target. It throws IllegalStateException 'Cannot enable reproducible timestamps. They can only be enabled when the target root doesn't exist or is an empty directory'.

Source

Thrown at jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/ZipUtil.java:66

    unzip(archive, destination, false);
  }

  /**
   * Unzips {@code archive} into {@code destination}.
   *
   * @param archive zip archive to unzip
   * @param destination target root for unzipping
   * @param enableReproducibleTimestamps whether or not reproducible timestamps should be used
   * @throws IOException when I/O error occurs
   * @throws IllegalStateException when reproducible timestamps are enabled but the target root used
   *     for unzipping is not empty
   */
  public static void unzip(Path archive, Path destination, boolean enableReproducibleTimestamps)
      throws IOException {
    if (enableReproducibleTimestamps
        && Files.isDirectory(destination)
        && destination.toFile().list().length != 0) {
      throw new IllegalStateException(
          "Cannot enable reproducible timestamps. They can only be enabled when the target root doesn't exist or is an empty directory");
    }
    String canonicalDestination = destination.toFile().getCanonicalPath();
    List<ZipEntry> entries = new ArrayList<>();
    try (InputStream fileIn = new BufferedInputStream(Files.newInputStream(archive));
        ZipInputStream zipIn = new ZipInputStream(fileIn)) {
      for (ZipEntry entry = zipIn.getNextEntry(); entry != null; entry = zipIn.getNextEntry()) {
        entries.add(entry);
        Path entryPath = destination.resolve(entry.getName());

        String canonicalTarget = entryPath.toFile().getCanonicalPath();
        if (!canonicalTarget.startsWith(canonicalDestination + File.separator)) {
          String offender = entry.getName() + " from " + archive;
          throw new IOException("Blocked unzipping files outside destination: " + offender);
        }

        if (entry.isDirectory()) {
          Files.createDirectories(entryPath);

View on GitHub (pinned to fb949e2676)

Solutions

  1. Delete the destination directory (or its contents) before calling unzip, e.g. with Jib's File deleter or Files.walk cleanup.
  2. Extract to a fresh temporary directory each run and swap it in afterwards.
  3. Pass enableReproducibleTimestamps=false only if reproducibility is not required and overwriting is acceptable.

Example fix

// before
ZipUtil.unzip(jarPath, targetDir, true);
// after
if (Files.isDirectory(targetDir)) {
  try (Stream<Path> walk = Files.walk(targetDir)) {
    walk.sorted(Comparator.reverseOrder()).forEach(p -> p.toFile().delete());
  }
}
ZipUtil.unzip(jarPath, targetDir, true);
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure destination is absent or empty before unzip
if (Files.isDirectory(destination)
    && destination.toFile().list() != null
    && destination.toFile().list().length > 0) {
  throw new IllegalStateException("destination not empty: " + destination);
}

Try / catch

try {
  ZipUtil.unzip(archive, destination, true);
} catch (IllegalStateException e) {
  // destination is a non-empty dir: clean it and retry
  FileUtils.deleteDirectory(destination.toFile());
  ZipUtil.unzip(archive, destination, true);
}

Prevention

When it happens

Trigger: Calling ZipUtil.unzip(archive, destination, true) where Files.isDirectory(destination) is true and destination.toFile().list().length != 0 — i.e. the target directory already contains files.

Common situations: Re-running a build that previously unpacked a dependency JAR (e.g. dependency-reduced exploded jars) into the same target directory; a stale or partially-cleaned build directory being reused; scripts reusing a shared extraction dir across builds.

Related errors


AI-assisted analysis of GoogleContainerTools/jib@fb949e2676 (2026-09-06). Data as JSON: /api/errors/4a2340e86f6c98cc. Report an issue: GitHub.