{"record":{"id":"adeeb022f27f61e5","repo":"GoogleContainerTools/jib","slug":"cannot-enable-reproducible-timestamps-they-can-on","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":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"jib-core/src/main/java/com/google/cloud/tools/jib/tar/TarExtractor.java","lineNumber":67,"sourceCode":"    extract(source, destination, false);\n  }\n\n  /**\n   * Extracts a tarball to the specified destination.\n   *\n   * @param source the tarball to extract\n   * @param destination the output directory\n   * @param enableReproducibleTimestamps whether or not reproducible timestamps should be used\n   * @throws IOException if extraction fails\n   * @throws IllegalStateException when reproducible timestamps are enabled but the target root used\n   *     for extracting the tar contents is not empty\n   */\n  public static void extract(Path source, 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<TarArchiveEntry> entries = new ArrayList<>();\n    try (InputStream in = new BufferedInputStream(Files.newInputStream(source));\n        TarArchiveInputStream tarArchiveInputStream = new TarArchiveInputStream(in)) {\n      for (TarArchiveEntry entry = tarArchiveInputStream.getNextEntry();\n          entry != null;\n          entry = tarArchiveInputStream.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 \" + source;\n          throw new IOException(\"Blocked unzipping files outside destination: \" + offender);\n        }\n        if (entry.isDirectory()) {","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/GoogleContainerTools/jib/blob/fb949e2676afbbd7dd7a1ef61e20251931325654/jib-core/src/main/java/com/google/cloud/tools/jib/tar/TarExtractor.java#L49-L85","documentation":"TarExtractor.extract throws IllegalStateException when reproducible timestamps are requested but the destination directory already exists and is non-empty. Reproducible extraction requires writing into a fresh location so entry order/timestamps are deterministic; Jib fails fast rather than mixing old and new content.","triggerScenarios":"Calling TarExtractor.extract(source, destination, true) where destination is an existing directory containing files; re-running extraction into the same folder with enableReproducibleTimestamps=true.","commonSituations":"Re-running a build/extraction script without cleaning the target dir; extracting layers into a cached workspace that already has prior contents.","solutions":["Delete or empty the destination directory before calling extract with enableReproducibleTimestamps=true.","Pass enableReproducibleTimestamps=false if merging into an existing directory is intentional.","Extract to a fresh temporary directory each run.","Add cleanup to your build script (e.g. gradle clean or rm -rf dest)."],"exampleFix":"// before\nTarExtractor.extract(tarPath, dest, true);\n// after\nFiles.walkFileTree(dest, deleteVisitor); // or FileUtils.deleteDirectory(dest)\nTarExtractor.extract(tarPath, dest, true);","handlingStrategy":"validation","validationCode":"// Ensure destination is fresh before reproducible extraction\nif (java.nio.file.Files.isDirectory(dest)\n    && java.util.Objects.requireNonNull(dest.toFile().list()).length > 0) {\n  throw new IllegalArgumentException(\"Destination must be empty: \" + dest);\n}","typeGuard":null,"tryCatchPattern":"try { TarExtractor.extract(src, dest, true); } catch (IllegalStateException e) { org.apache.commons.io.FileUtils.deleteDirectory(dest.toFile()); TarExtractor.extract(src, dest, true); }","preventionTips":["Clean the destination before each reproducible extraction.","Extract into a new temp directory per run.","Pass enableReproducibleTimestamps=false for in-place merges.","Add a pre-build cleanup step to scripts/CI."],"tags":["tar","reproducibility","filesystem"],"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"}