java-native-access/jna · error · IllegalArgumentException

destDir must be a directory.

Error message

destDir must be a directory.

What it means

Advapi32Util.backupEncryptedFile(File src, File destDir) backs up an EFS-encrypted file via the Win32 OpenEncryptedFileRaw/ReadEncryptedFileRaw API, which requires a destination directory to write the backup into. If destDir does not exist or is a regular file, the method throws IllegalArgumentException('destDir must be a directory.') before starting the backup.

Source

Thrown at contrib/platform/src/com/sun/jna/platform/win32/Advapi32Util.java:3134

        }
    }

    /**
     * Backup an encrypted file or folder without decrypting it. A file named
     * "bar/sample.text" will be backed-up to "destDir/sample.text". A directory
     * named "bar" will be backed-up to "destDir/bar". This method is NOT
     * recursive. If you have an encrypted directory with encrypted files, this
     * method must be called once for the directory, and once for each encrypted
     * file to be backed-up.
     *
     * @param src
     *         The encrypted file or directory to backup.
     * @param destDir
     *         The directory where the backup will be saved.
     */
    public static void backupEncryptedFile(File src, File destDir) {
        if (!destDir.isDirectory()) {
            throw new IllegalArgumentException("destDir must be a directory.");
        }

        ULONG readFlag = new ULONG(0); // Open the file for export (backup)
        ULONG writeFlag = new ULONG(CREATE_FOR_IMPORT); // Import (restore) file

        if (src.isDirectory()) {
            writeFlag.setValue(CREATE_FOR_IMPORT | CREATE_FOR_DIR);
        }

        // open encrypted file for export
        String srcFileName = src.getAbsolutePath();
        PointerByReference pvContext = new PointerByReference();
        if (Advapi32.INSTANCE.OpenEncryptedFileRaw(srcFileName, readFlag,
                pvContext) != W32Errors.ERROR_SUCCESS) {
            throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
        }

        // read encrypted file

View on GitHub (pinned to d036ad9781)

Solutions

  1. Create the destination directory before the call: if (!destDir.exists()) destDir.mkdirs();
  2. Pass the parent directory (destDir) not the output file path; the method writes the backup into the directory itself.
  3. Verify with destDir.isDirectory() and correct the path (check for typos, drive letters, UNC paths).
  4. If destDir is a file, delete or rename it and create a directory with the same path.

Example fix

// before
Advapi32Util.backupEncryptedFile(src, new File("C:\\backup\\out.dat"));
// after
File destDir = new File("C:\\backup");
if (!destDir.isDirectory()) {
    destDir.mkdirs();
}
Advapi32Util.backupEncryptedFile(src, destDir);
Defensive patterns

Strategy: validation

Validate before calling

if (destDir == null || !destDir.isDirectory()) {
    throw new IllegalArgumentException("destDir must be an existing directory: " + destDir);
}

Type guard

boolean isWritableDirectory(File dir) {
    return dir != null && dir.isDirectory() && dir.canWrite();
}

Try / catch

try {
    Advapi32Util.backupEncryptedFile(src, destDir);
} catch (IllegalArgumentException e) {
    log.error("Bad destination for EFS backup: " + e.getMessage());
}

Prevention

When it happens

Trigger: Calling backupEncryptedFile with a destDir path that does not exist, points to a file instead of a directory, or is a stale path (deleted after creation).

Common situations: Typo'd or uncreated output folder; passing the intended output file path rather than the containing directory; running the backup before the destination mount/dir is prepared; path separator confusion on Windows.

Related errors


AI-assisted analysis of java-native-access/jna@d036ad9781 (2026-09-12). Data as JSON: /api/errors/47324bea80e0e401. Report an issue: GitHub.