apache/pulsar · error · IOException

Cannot create ${narExtractionTempDirectory}

Error message

Cannot create ${narExtractionTempDirectory}

What it means

NarUnpacker unpacks a NAR bundle into a temp directory named '<sha-checksum>.tmp' inside the NAR extraction parent directory before atomically renaming it. If File.mkdir() cannot create that temp directory, an IOException('Cannot create <path>') is thrown. mkdir() fails when the parent directory does not exist, is not writable, or a filesystem error (e.g. permissions, disk full, name collision as a non-directory) prevents creation.

Source

Thrown at pulsar-common/src/main/java/org/apache/pulsar/common/nar/NarUnpacker.java:95

        }
        String checksum = Base64.getUrlEncoder().withoutPadding().encodeToString(FileUtils.calculateSha256sum(nar));
        // ensure that one process can extract the files
        File lockFile = new File(parentDirectory, "." + checksum + ".lock");
        // prevent OverlappingFileLockException by ensuring that one thread tries to create a lock in this JVM
        Object localLock = CURRENT_JVM_FILE_LOCKS.computeIfAbsent(lockFile.getAbsolutePath(), key -> new Object());
        synchronized (localLock) {
            // create file lock that ensures that other processes
            // using the same lock file don't execute concurrently
            try (FileChannel channel = new RandomAccessFile(lockFile, "rw").getChannel();
                 FileLock lock = channel.lock()) {
                File narWorkingDirectory = new File(parentDirectory, checksum);
                if (!narWorkingDirectory.exists()) {
                    File narExtractionTempDirectory = new File(parentDirectory, checksum + ".tmp");
                    if (narExtractionTempDirectory.exists()) {
                        FileUtils.deleteFile(narExtractionTempDirectory, true);
                    }
                    if (!narExtractionTempDirectory.mkdir()) {
                        throw new IOException("Cannot create " + narExtractionTempDirectory);
                    }
                    try {
                        log.info().attr("nar", nar).attr("destination", narExtractionTempDirectory).log("Extracting");
                        if (extractCallback != null) {
                            extractCallback.run();
                        }
                        unpack(nar, narExtractionTempDirectory);
                    } catch (IOException e) {
                        log.error()
                                .attr("directory", narExtractionTempDirectory)
                                .exception(e)
                                .log("There was a problem extracting the nar file. Deleting to clean up state.");
                        try {
                            FileUtils.deleteFile(narExtractionTempDirectory, true);
                        } catch (IOException e2) {
                            log.error()
                                    .attr("directory", narExtractionTempDirectory)
                                    .exception(e2)

View on GitHub (pinned to 820761864e)

Solutions

  1. Verify the NAR extraction directory (pulsar.nar.extraction.path or java.io.tmpdir based) exists and is writable by the process user; create it with mkdir -p and chown if needed.
  2. Check filesystem free space and mount flags (not read-only, not full).
  3. Delete any stale '<checksum>.tmp' files or directories in the parent directory and restart.
  4. Run the process under a user that owns or can write the extraction directory; on containers mount a writable volume at the extraction path.
  5. Catch the IOException in the caller and retry after correcting the environment; unpackNar uses per-checksum file locks so concurrent runs are safe once the directory is writable.

Example fix

// before (failing env)
ProcessBuilder pb = ...; // runs with pulsar.nar.extraction.path=/pulsar/nar owned by root
// after
Files.createDirectories(Paths.get("/pulsar/nar"));
Files.setPosixFilePermissions(Paths.get("/pulsar/nar"), PosixFilePermissions.fromString("rwxr-xr-x"));
Defensive patterns

Strategy: validation

Validate before calling

File dir = new File(narExtractionPath);
if (!dir.isDirectory() || !dir.canWrite()) {
    throw new IllegalStateException("NAR extraction dir missing or not writable: " + dir);
}
File tmp = new File(dir, checksum + ".tmp");
if (tmp.exists() && !tmp.isDirectory()) tmp.delete();

Type guard

static boolean canCreateIn(File dir) {
    return dir.isDirectory() && dir.canWrite();
}

Try / catch

try {
    Path unpacked = NarUnpacker.unpackNar(narFile, extractionDir);
} catch (IOException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Cannot create")) {
        // fix permissions/space for extraction dir, then retry
    }
    throw new UncheckedIOException(e);
}

Prevention

When it happens

Trigger: Calling NarUnpacker.unpackNar(...) when the parent extraction directory was deleted or is read-only (e.g. running as a non-root user against a root-owned temp dir, read-only container filesystem, or disk-full), or a stale '<checksum>.tmp' path exists as a regular file that could not be cleaned.

Common situations: Kubernetes/container deployments with a read-only or tiny emptyDir for the nar extraction path; permission changes after running the broker once as root; leftover stale .tmp entries from a crashed previous run.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/157ac6322e88eaaf. Report an issue: GitHub.