apache/pulsar · error · IOException

Cannot create ${parentDirectory}

Error message

Cannot create ${parentDirectory}

What it means

NarUnpacker.doUnpackNar creates '<base>/<nar-name>-unpacked' as the extraction target. If the directory doesn't exist, it calls mkdirs(); if mkdirs() fails AND the directory still doesn't exist, it throws IOException 'Cannot create <parentDirectory>'. This usually means the base working directory is not writable or another process created/removed the path concurrently.

Source

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

     * @param baseWorkingDirectory
     *            the directory to unpack to
     * @return the directory to the unpacked NAR
     * @throws IOException
     *             if unable to explode nar
     */
    public static File unpackNar(final File nar, final File baseWorkingDirectory) throws IOException {
        return doUnpackNar(nar, baseWorkingDirectory, null);
    }

    @VisibleForTesting
    static File doUnpackNar(final File nar, final File baseWorkingDirectory, Runnable extractCallback)
            throws IOException {
        File parentDirectory = new File(baseWorkingDirectory, nar.getName() + "-unpacked");
        if (!parentDirectory.exists()) {
            if (parentDirectory.mkdirs()) {
                log.info().attr("directory", parentDirectory).log("Created directory");
            } else if (!parentDirectory.exists()) {
                throw new IOException("Cannot create " + parentDirectory);
            }
        }
        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);
                    }

View on GitHub (pinned to 820761864e)

Solutions

  1. Set narExtractionDirectory (or the equivalent base dir) to a path writable by the service user.
  2. Pre-create the base directory with correct ownership (mkdir -p && chown).
  3. In containers, mount an emptyDir/volume at the extraction directory.
  4. Check disk space (df -h) and for concurrent processes stomping the same directory.

Example fix

// before
NarUnpacker unpacker = new NarUnpacker(..., new File("/root/nar-work")); // not writable
// after
NarUnpacker unpacker = new NarUnpacker(..., new File("/var/lib/pulsar/nar-work")); // chown'd to service user
Defensive patterns

Strategy: validation

Validate before calling

File base = new File(extractionDir);
if (!base.isDirectory() || !base.canWrite()) {
    throw new IllegalStateException("NAR extraction dir missing or not writable: " + extractionDir);
}

Try / catch

try {
    narUnpacker.unpack(narFile, outputStream);
} catch (IOException e) {
    if (e.getMessage().startsWith("Cannot create")) {
        log.error("Extraction target unwritable: {} (check narExtractionDirectory ownership/mount)",
            e.getMessage());
    }
    throw e;
}

Prevention

When it happens

Trigger: unpackNar invoked with a baseWorkingDirectory that is read-only, owned by another user, or on a full/read-only filesystem, so mkdirs() for the '-unpacked' directory fails.

Common situations: Function worker / broker narExtractionDirectory set to a root-owned or read-only path, running in a container with readOnlyRootFilesystem and no writable volume, or two broker instances sharing one extraction directory with conflicting permissions.

Related errors


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