{"record":{"id":"4a2340e86f6c98cc","repo":"GoogleContainerTools/jib","slug":"cannot-enable-reproducible-timestamps-they-can-on-4a2340","errorCode":null,"errorMessage":"Cannot enable reproducible timestamps. They can only be enabled when the target root doesn't exist or is an empty directory","messagePattern":"Cannot enable reproducible timestamps\\. They can only be enabled when the target root doesn't exist or is an empty directory","errorType":"validation","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/ZipUtil.java","lineNumber":66,"sourceCode":"    unzip(archive, destination, false);\n  }\n\n  /**\n   * Unzips {@code archive} into {@code destination}.\n   *\n   * @param archive zip archive to unzip\n   * @param destination target root for unzipping\n   * @param enableReproducibleTimestamps whether or not reproducible timestamps should be used\n   * @throws IOException when I/O error occurs\n   * @throws IllegalStateException when reproducible timestamps are enabled but the target root used\n   *     for unzipping is not empty\n   */\n  public static void unzip(Path archive, Path destination, boolean enableReproducibleTimestamps)\n      throws IOException {\n    if (enableReproducibleTimestamps\n        && Files.isDirectory(destination)\n        && destination.toFile().list().length != 0) {\n      throw new IllegalStateException(\n          \"Cannot enable reproducible timestamps. They can only be enabled when the target root doesn't exist or is an empty directory\");\n    }\n    String canonicalDestination = destination.toFile().getCanonicalPath();\n    List<ZipEntry> entries = new ArrayList<>();\n    try (InputStream fileIn = new BufferedInputStream(Files.newInputStream(archive));\n        ZipInputStream zipIn = new ZipInputStream(fileIn)) {\n      for (ZipEntry entry = zipIn.getNextEntry(); entry != null; entry = zipIn.getNextEntry()) {\n        entries.add(entry);\n        Path entryPath = destination.resolve(entry.getName());\n\n        String canonicalTarget = entryPath.toFile().getCanonicalPath();\n        if (!canonicalTarget.startsWith(canonicalDestination + File.separator)) {\n          String offender = entry.getName() + \" from \" + archive;\n          throw new IOException(\"Blocked unzipping files outside destination: \" + offender);\n        }\n\n        if (entry.isDirectory()) {\n          Files.createDirectories(entryPath);","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/GoogleContainerTools/jib/blob/fb949e2676afbbd7dd7a1ef61e20251931325654/jib-plugins-common/src/main/java/com/google/cloud/tools/jib/plugins/common/ZipUtil.java#L48-L84","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Delete the destination directory (or its contents) before calling unzip, e.g. with Jib's File deleter or Files.walk cleanup.","Extract to a fresh temporary directory each run and swap it in afterwards.","Pass enableReproducibleTimestamps=false only if reproducibility is not required and overwriting is acceptable."],"exampleFix":"// before\nZipUtil.unzip(jarPath, targetDir, true);\n// after\nif (Files.isDirectory(targetDir)) {\n  try (Stream<Path> walk = Files.walk(targetDir)) {\n    walk.sorted(Comparator.reverseOrder()).forEach(p -> p.toFile().delete());\n  }\n}\nZipUtil.unzip(jarPath, targetDir, true);","handlingStrategy":"try-catch","validationCode":"// Ensure destination is absent or empty before unzip\nif (Files.isDirectory(destination)\n    && destination.toFile().list() != null\n    && destination.toFile().list().length > 0) {\n  throw new IllegalStateException(\"destination not empty: \" + destination);\n}","typeGuard":null,"tryCatchPattern":"try {\n  ZipUtil.unzip(archive, destination, true);\n} catch (IllegalStateException e) {\n  // destination is a non-empty dir: clean it and retry\n  FileUtils.deleteDirectory(destination.toFile());\n  ZipUtil.unzip(archive, destination, true);\n}","preventionTips":["Clean the target directory (or use a fresh temp dir) before every unzip","Never reuse an extraction directory across builds with reproducible timestamps enabled","Add a clean step to CI before exploded-jar extraction"],"tags":["zip","file-extraction","reproducibility","java"],"backgroundTag":"directory-not-empty","analyzedSha":"fb949e2676afbbd7dd7a1ef61e20251931325654","analyzedAt":"2026-09-06T14:04:09.491Z","contentChangedAt":"2026-09-06T14:04:09.491Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}