{"record":{"id":"91cb3eca97ea4605","repo":"java-native-access/jna","slug":"win32exception-from-native-getlasterror-after-91cb3e","errorCode":null,"errorMessage":"Win32Exception from native GetLastError after ExpandEnvironmentStrings returned 0 (first call)","messagePattern":"Win32Exception from native GetLastError after ExpandEnvironmentStrings returned 0 \\(first call\\)","errorType":"error_code","errorClass":"Win32Exception","httpStatus":null,"severity":"error","filePath":"contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java","lineNumber":1236,"sourceCode":"     *              name. If the name is not found, the %variableName% portion\n     *              is left unexpanded.</p>\n     *\n     *              <p>Note that this function does not support all the features\n     *              that Cmd.exe supports. For example, it does not support\n     *              %variableName:str1=str2% or %variableName:~offset,length%.</p>\n     *\n     * @return the replaced string\n     * @throws Win32Exception if an error occurs\n     */\n    public static String expandEnvironmentStrings(String input) {\n        if(input == null) {\n            return \"\";\n        }\n\n        int resultChars = Kernel32.INSTANCE.ExpandEnvironmentStrings(input, null, 0);\n\n        if(resultChars == 0) {\n            throw new Win32Exception(Kernel32.INSTANCE.GetLastError());\n        }\n\n        Memory resultMemory;\n        if( W32APITypeMapper.DEFAULT == W32APITypeMapper.UNICODE ) {\n            resultMemory = new Memory(resultChars * Native.WCHAR_SIZE);\n        } else {\n            // return value is length in chars including terminating NULL,\n            // documentation for ANSI version says: buffer size should be the\n            // string length, plus terminating null character, plus one\n            resultMemory = new Memory(resultChars + 1);\n        }\n        resultChars = Kernel32.INSTANCE.ExpandEnvironmentStrings(input, resultMemory, resultChars);\n\n        if(resultChars == 0) {\n            throw new Win32Exception(Kernel32.INSTANCE.GetLastError());\n        }\n\n        if( W32APITypeMapper.DEFAULT == W32APITypeMapper.UNICODE ) {","sourceCodeStart":1218,"sourceCodeEnd":1254,"githubUrl":"https://github.com/java-native-access/jna/blob/d036ad9781adad4b66693e8fa7098e4ac665e0a3/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java#L1218-L1254","documentation":"Kernel32Util.expandEnvironmentStrings() first calls the Win32 ExpandEnvironmentStrings API with a null buffer and size 0 to query the required character count. If the API returns 0, the call failed and the library wraps the native GetLastError() code into a com.sun.jna.platform.win32.Win32Exception so the developer gets the actual Windows error (e.g. ERROR_ENVVAR_NOT_FOUND is not typical here; more commonly a memory/format error) instead of a bare 0 return.","triggerScenarios":"Calling Kernel32Util.expandEnvironmentStrings(String) when the first (size-query) invocation of ExpandEnvironmentStrings fails on Windows, i.e. returns 0. This happens when the native call itself errors out, e.g. input buffer cannot be processed or a native-level fault occurs before expansion.","commonSituations":"Running JNA platform code on Windows where the environment-string expansion query fails; often seen when the input string is malformed for the API or when running under restricted environments that make native calls fail; also surfaces in tests that mock Kernel32 and force a 0 return.","solutions":["Inspect Win32Exception.getErrorCode() and cross-reference it with the Win32 error list to find the native cause.","Verify the input string is a valid, non-corrupt path/string with %VAR% references that ExpandEnvironmentStrings can process.","Confirm the code runs on Windows with a functioning kernel32 and JNA native access (no security policy blocking native calls).","If running in a mocked/test harness, ensure the mock returns a nonzero required-size value for the first call."],"exampleFix":"// before\nString expanded = Kernel32Util.expandEnvironmentStrings(userSuppliedPath);\n// after\nString expanded;\ntry {\n    expanded = Kernel32Util.expandEnvironmentStrings(userSuppliedPath);\n} catch (Win32Exception e) {\n    LOG.warn(\"Environment expansion failed with Win32 error \" + e.getErrorCode() + \", using raw path\");\n    expanded = userSuppliedPath;\n}","handlingStrategy":"try-catch","validationCode":"if (input == null || input.isEmpty()) {\n    throw new IllegalArgumentException(\"expandEnvironmentStrings requires a non-empty input\");\n}\nif (!Platform.isWindows()) {\n    throw new IllegalStateException(\"expandEnvironmentStrings is Windows-only\");\n}","typeGuard":"boolean isExpandableInput(String s) { return s != null && !s.isEmpty(); }","tryCatchPattern":"try {\n    result = Kernel32Util.expandEnvironmentStrings(input);\n} catch (Win32Exception e) {\n    log.warn(\"ExpandEnvironmentStrings failed, Win32 code {}\", e.getErrorCode());\n    result = input; // fall back to unexpanded\n}","preventionTips":["Always handle Win32Exception around native wrapper utilities — they surface OS failures.","Log e.getErrorCode() and map it to a Win32 error name for diagnosis.","Only run this code on Windows (guard with Platform.isWindows()).","Provide an unexpanded-string fallback path for non-critical expansions."],"tags":["win32","jna","windows","native-call","environment-strings"],"backgroundTag":"api-error-response","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"}