{"record":{"id":"77b07e9948d6827a","repo":"java-native-access/jna","slug":"win32exception-from-native-getlasterror-on-retry-of","errorCode":null,"errorMessage":"Win32Exception from native GetLastError on retry of GetVolumePathNamesForVolumeName","messagePattern":"Win32Exception from native GetLastError on retry of GetVolumePathNamesForVolumeName","errorType":"error_code","errorClass":"Win32Exception","httpStatus":null,"severity":"error","filePath":"contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java","lineNumber":877,"sourceCode":"     * @param lpszVolumeName The volume name\n     * @return The parsed result\n     * @throws Win32Exception If failed to retrieve the required information\n     */\n    public static final List<String> getVolumePathNamesForVolumeName(String lpszVolumeName) {\n        char[] lpszVolumePathNames = new char[WinDef.MAX_PATH + 1];\n        IntByReference lpcchReturnLength = new IntByReference();\n\n        if (!Kernel32.INSTANCE.GetVolumePathNamesForVolumeName(lpszVolumeName, lpszVolumePathNames, lpszVolumePathNames.length, lpcchReturnLength)) {\n            int hr = Kernel32.INSTANCE.GetLastError();\n            if (hr != WinError.ERROR_MORE_DATA) {\n                throw new Win32Exception(hr);\n            }\n\n            int required = lpcchReturnLength.getValue();\n            lpszVolumePathNames = new char[required];\n            // this time we MUST succeed\n            if (!Kernel32.INSTANCE.GetVolumePathNamesForVolumeName(lpszVolumeName, lpszVolumePathNames, lpszVolumePathNames.length, lpcchReturnLength)) {\n                throw new Win32Exception(Kernel32.INSTANCE.GetLastError());\n            }\n        }\n\n        int bufSize = lpcchReturnLength.getValue();\n        return Native.toStringList(lpszVolumePathNames, 0, bufSize);\n    }\n\n    // prefix and suffix of a volume GUID path\n    public static final String VOLUME_GUID_PATH_PREFIX = \"\\\\\\\\?\\\\Volume{\";\n    public static final String VOLUME_GUID_PATH_SUFFIX = \"}\\\\\";\n\n    /**\n     * Parses and returns the pure GUID value of a volume name obtained\n     * from {@link Kernel32#FindFirstVolume(char[], int)} or\n     * {@link Kernel32#FindNextVolume} calls\n     *\n     * @param volumeGUIDPath\n     *              The volume GUID path as returned by one of the above mentioned calls","sourceCodeStart":859,"sourceCodeEnd":895,"githubUrl":"https://github.com/java-native-access/jna/blob/d036ad9781adad4b66693e8fa7098e4ac665e0a3/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java#L859-L895","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Retry the whole two-call sequence once more — transient race with volume unmount is the usual cause.","Re-enumerate the volume GUID freshly before retrying; the old GUID may be stale.","Catch Win32Exception and fall back to enumerating all volumes (FindFirstVolume) to rebuild current state.","If reproducible, log the error code: ERROR_ACCESS_DENIED points to permissions, not a race."],"exampleFix":"// before\nString[] paths = Kernel32Util.getVolumePathNamesForVolumeName(guid); // throws on transient race\n\n// after\nString[] paths = null;\nfor (int attempt = 0; attempt < 2 && paths == null; attempt++) {\n    try {\n        paths = Kernel32Util.getVolumePathNamesForVolumeName(guid);\n    } catch (Win32Exception e) {\n        if (attempt == 1) throw e;\n        Thread.sleep(50); // volume state may settle\n    }\n}","handlingStrategy":"retry","validationCode":"String freshGuid = Kernel32Util.getVolumeNameForVolumeMountPoint(mountPoint); // refresh before use","typeGuard":null,"tryCatchPattern":"for (int i = 0; i < 3; i++) {\n    try {\n        return Kernel32Util.getVolumePathNamesForVolumeName(guid);\n    } catch (Win32Exception e) {\n        if (i == 2) throw e;\n        Thread.sleep(50);\n    }\n}","preventionTips":["Expect transient races with hot-pluggable volumes and wrap calls in bounded retries.","Refresh volume GUIDs from the OS before retrying.","Distinguish ERROR_ACCESS_DENIED (permissions) from race conditions via error code.","Avoid unmounting/enumerating volumes concurrently from multiple threads."],"tags":["win32","volume","retry","jna"],"backgroundTag":"win32-native-error","analyzedSha":"d036ad9781adad4b66693e8fa7098e4ac665e0a3","analyzedAt":"2026-09-12T06:50:59.239Z","contentChangedAt":"2026-09-12T06:50:59.239Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}