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
- Ensure the input is a full volume GUID path of the form \\?\Volume{GUID}\ — use Kernel32Util.extractVolumeGUID/PathByGuid to validate or build it.
- Re-enumerate volumes via Kernel32.INSTANCE.FindFirstVolume/FindNextVolume instead of trusting cached GUIDs.
- Catch Win32Exception and check for ERROR_FILE_NOT_FOUND/ERROR_INVALID_NAME to distinguish missing vs malformed input.
- 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
- Only pass paths obtained from FindFirstVolume/FindNextVolume.
- Re-enumerate volumes rather than caching GUIDs across sessions.
- Validate the \\?\Volume{...}\ format before calling.
- Handle removable volumes disappearing at any moment.
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
- Win32Exception from native GetLastError on retry of GetVolum
- Bad volume GUID path format:
- LookupAccountNameW was expected to fail with ERROR_INSUFFICI
- Expected GetTokenInformation to fail with ERROR_INSUFFICIENT
- Failed to find privilege "{privilege}" - {GetLastError}
AI-assisted analysis of java-native-access/jna@d036ad9781 (2026-09-12).
Data as JSON: /api/errors/aed2ac514d927127.
Report an issue: GitHub.