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
- Set narExtractionDirectory (or the equivalent base dir) to a path writable by the service user.
- Pre-create the base directory with correct ownership (mkdir -p && chown).
- In containers, mount an emptyDir/volume at the extraction directory.
- 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
- Set narExtractionDirectory to a writable, service-user-owned path at deployment time
- Mount an emptyDir/persistent volume at the extraction path in containers
- Give each broker/worker instance its own extraction directory to avoid shared-dir conflicts
- Check disk space and mount ro flags before startup
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
- ${dir} is not a directory
- ${dir} directory does not have read/write privilege
- Cannot create ${narExtractionTempDirectory}
- Failed to load the additional servlet for name `${servletNam
- Failed to read decryption key from ${keyUri}
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/69ca563ab48eef7d.
Report an issue: GitHub.