java-native-access/jna · error · Win32Exception

Win32Exception from GetLastError after GetVolumePathNamesFor

Error message

Win32Exception from GetLastError after GetVolumePathNamesForVolumeName failed (not ERROR_MORE_DATA)

What it means

getVolumePathNamesForVolumeName maps a volume GUID (\\?\Volume{...}\) to its mount point paths using GetVolumePathNamesForVolumeName. The wrapper first calls with a MAX_PATH+1 buffer; if the call fails with any error other than ERROR_MORE_DATA (meaning 'buffer too small, retry'), it throws Win32Exception with the native GetLastError code. This is a hard failure of the volume lookup, not the expected grow-and-retry path.

Source

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

        }

        return Native.toStringList(lpTargetPath, 0, dwSize);
    }

    /**
     * Invokes and parses the result of {@link Kernel32#GetVolumePathNamesForVolumeName(String, char[], int, IntByReference)}
     * @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 = "}\\";

View on GitHub (pinned to d036ad9781)

Solutions

  1. Ensure the input is a full volume GUID path of the form \\?\Volume{GUID}\ — use Kernel32Util.extractVolumeGUID/PathByGuid to validate or build it.
  2. Re-enumerate volumes via Kernel32.INSTANCE.FindFirstVolume/FindNextVolume instead of trusting cached GUIDs.
  3. Catch Win32Exception and check for ERROR_FILE_NOT_FOUND/ERROR_INVALID_NAME to distinguish missing vs malformed input.
  4. Run under an account with volume query privileges if ERROR_ACCESS_DENIED is returned.

Example fix

// before
String[] paths = Kernel32Util.getVolumePathNamesForVolumeName("C:\\"); // wrong: not a GUID path

// after
String guidPath = "\\\\?\\Volume{abcd1234-...}\\";
try {
    String[] paths = Kernel32Util.getVolumePathNamesForVolumeName(guidPath);
} catch (Win32Exception e) {
    if (e.getErrorCode() == WinError.ERROR_FILE_NOT_FOUND) {
        paths = new String[0]; // volume no longer present
    } else {
        throw e;
    }
}
Defensive patterns

Strategy: validation

Validate before calling

if (volumeGUIDPath == null
        || !volumeGUIDPath.matches("^\\\\\\?\\\\Volume\\{[0-9a-fA-F\\-]+\\}\\\\$")) {
    throw new IllegalArgumentException("Not a volume GUID path: " + volumeGUIDPath);
}

Type guard

static boolean isVolumeGuidPath(String s) {
    return s != null && s.matches("^\\\\\\?\\\\Volume\\{[0-9a-fA-F\\-]+\\}\\\\$");
}

Try / catch

try {
    return Kernel32Util.getVolumePathNamesForVolumeName(guid);
} catch (Win32Exception e) {
    if (e.getErrorCode() == WinError.ERROR_FILE_NOT_FOUND) {
        return new String[0];
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling Kernel32Util.getVolumePathNamesForVolumeName(volumeGUID) when the first GetVolumePathNamesForVolumeName call returns FALSE with GetLastError != ERROR_MORE_DATA — the volume GUID string is malformed, the volume does not exist or was unmounted, or access to the volume manager is denied.

Common situations: Passing a drive letter ("C:\") instead of a volume GUID path; GUID string from a stale registry entry or old enumeration after the volume was removed; USB/removable volume unplugged; running without privileges to query volume information.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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