{"record":{"id":"69ca563ab48eef7d","repo":"apache/pulsar","slug":"cannot-create-parentdirectory","errorCode":null,"errorMessage":"Cannot create ${parentDirectory}","messagePattern":"Cannot create (.+?)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"pulsar-common/src/main/java/org/apache/pulsar/common/nar/NarUnpacker.java","lineNumber":75,"sourceCode":"     * @param baseWorkingDirectory\n     *            the directory to unpack to\n     * @return the directory to the unpacked NAR\n     * @throws IOException\n     *             if unable to explode nar\n     */\n    public static File unpackNar(final File nar, final File baseWorkingDirectory) throws IOException {\n        return doUnpackNar(nar, baseWorkingDirectory, null);\n    }\n\n    @VisibleForTesting\n    static File doUnpackNar(final File nar, final File baseWorkingDirectory, Runnable extractCallback)\n            throws IOException {\n        File parentDirectory = new File(baseWorkingDirectory, nar.getName() + \"-unpacked\");\n        if (!parentDirectory.exists()) {\n            if (parentDirectory.mkdirs()) {\n                log.info().attr(\"directory\", parentDirectory).log(\"Created directory\");\n            } else if (!parentDirectory.exists()) {\n                throw new IOException(\"Cannot create \" + parentDirectory);\n            }\n        }\n        String checksum = Base64.getUrlEncoder().withoutPadding().encodeToString(FileUtils.calculateSha256sum(nar));\n        // ensure that one process can extract the files\n        File lockFile = new File(parentDirectory, \".\" + checksum + \".lock\");\n        // prevent OverlappingFileLockException by ensuring that one thread tries to create a lock in this JVM\n        Object localLock = CURRENT_JVM_FILE_LOCKS.computeIfAbsent(lockFile.getAbsolutePath(), key -> new Object());\n        synchronized (localLock) {\n            // create file lock that ensures that other processes\n            // using the same lock file don't execute concurrently\n            try (FileChannel channel = new RandomAccessFile(lockFile, \"rw\").getChannel();\n                 FileLock lock = channel.lock()) {\n                File narWorkingDirectory = new File(parentDirectory, checksum);\n                if (!narWorkingDirectory.exists()) {\n                    File narExtractionTempDirectory = new File(parentDirectory, checksum + \".tmp\");\n                    if (narExtractionTempDirectory.exists()) {\n                        FileUtils.deleteFile(narExtractionTempDirectory, true);\n                    }","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-common/src/main/java/org/apache/pulsar/common/nar/NarUnpacker.java#L57-L93","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nNarUnpacker unpacker = new NarUnpacker(..., new File(\"/root/nar-work\")); // not writable\n// after\nNarUnpacker unpacker = new NarUnpacker(..., new File(\"/var/lib/pulsar/nar-work\")); // chown'd to service user","handlingStrategy":"validation","validationCode":"File base = new File(extractionDir);\nif (!base.isDirectory() || !base.canWrite()) {\n    throw new IllegalStateException(\"NAR extraction dir missing or not writable: \" + extractionDir);\n}","typeGuard":null,"tryCatchPattern":"try {\n    narUnpacker.unpack(narFile, outputStream);\n} catch (IOException e) {\n    if (e.getMessage().startsWith(\"Cannot create\")) {\n        log.error(\"Extraction target unwritable: {} (check narExtractionDirectory ownership/mount)\",\n            e.getMessage());\n    }\n    throw e;\n}","preventionTips":["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"],"tags":["filesystem","io","nar","unpack","directory"],"backgroundTag":"directory-creation-failed","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}