java-native-access/jna · error · Win32Exception

Win32Exception from native GetLastError on retry of GetVolum

Error message

Win32Exception from native GetLastError on retry of GetVolumePathNamesForVolumeName

What it means

This is the retry branch of getVolumePathNamesForVolumeName: after the first call fails with ERROR_MORE_DATA, the wrapper reallocates the buffer to lpcchReturnLength characters and calls the API again, expecting guaranteed success. If the retry still fails, Win32Exception is thrown with the latest GetLastError code. In practice this indicates the volume state changed between calls (return length shrank/invalidated) or a genuine access error surfaced on the second attempt.

Source

Thrown at contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java:877

     * @param lpszVolumeName The volume name
     * @return The parsed result
     * @throws Win32Exception If failed to retrieve the required information
     */
    public static final List<String> getVolumePathNamesForVolumeName(String lpszVolumeName) {
        char[] lpszVolumePathNames = new char[WinDef.MAX_PATH + 1];
        IntByReference lpcchReturnLength = new IntByReference();

        if (!Kernel32.INSTANCE.GetVolumePathNamesForVolumeName(lpszVolumeName, lpszVolumePathNames, lpszVolumePathNames.length, lpcchReturnLength)) {
            int hr = Kernel32.INSTANCE.GetLastError();
            if (hr != WinError.ERROR_MORE_DATA) {
                throw new Win32Exception(hr);
            }

            int required = lpcchReturnLength.getValue();
            lpszVolumePathNames = new char[required];
            // this time we MUST succeed
            if (!Kernel32.INSTANCE.GetVolumePathNamesForVolumeName(lpszVolumeName, lpszVolumePathNames, lpszVolumePathNames.length, lpcchReturnLength)) {
                throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
            }
        }

        int bufSize = lpcchReturnLength.getValue();
        return Native.toStringList(lpszVolumePathNames, 0, bufSize);
    }

    // prefix and suffix of a volume GUID path
    public static final String VOLUME_GUID_PATH_PREFIX = "\\\\?\\Volume{";
    public static final String VOLUME_GUID_PATH_SUFFIX = "}\\";

    /**
     * Parses and returns the pure GUID value of a volume name obtained
     * from {@link Kernel32#FindFirstVolume(char[], int)} or
     * {@link Kernel32#FindNextVolume} calls
     *
     * @param volumeGUIDPath
     *              The volume GUID path as returned by one of the above mentioned calls

View on GitHub (pinned to d036ad9781)

Solutions

  1. Retry the whole two-call sequence once more — transient race with volume unmount is the usual cause.
  2. Re-enumerate the volume GUID freshly before retrying; the old GUID may be stale.
  3. Catch Win32Exception and fall back to enumerating all volumes (FindFirstVolume) to rebuild current state.
  4. If reproducible, log the error code: ERROR_ACCESS_DENIED points to permissions, not a race.

Example fix

// before
String[] paths = Kernel32Util.getVolumePathNamesForVolumeName(guid); // throws on transient race

// after
String[] paths = null;
for (int attempt = 0; attempt < 2 && paths == null; attempt++) {
    try {
        paths = Kernel32Util.getVolumePathNamesForVolumeName(guid);
    } catch (Win32Exception e) {
        if (attempt == 1) throw e;
        Thread.sleep(50); // volume state may settle
    }
}
Defensive patterns

Strategy: retry

Validate before calling

String freshGuid = Kernel32Util.getVolumeNameForVolumeMountPoint(mountPoint); // refresh before use

Try / catch

for (int i = 0; i < 3; i++) {
    try {
        return Kernel32Util.getVolumePathNamesForVolumeName(guid);
    } catch (Win32Exception e) {
        if (i == 2) throw e;
        Thread.sleep(50);
    }
}

Prevention

When it happens

Trigger: Second GetVolumePathNamesForVolumeName call (after ERROR_MORE_DATA resize) returns FALSE — the volume was unmounted between calls, the reported required length became stale, or access was denied on retry.

Common situations: USB/network volumes being unmounted concurrently by another process or user; hot-plug device removal while enumerating mounts; race conditions in scripts that mount/unmount drives; rarely, antivirus interference on the volume query path.

Related errors


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