java-native-access/jna · error · Win32Exception
Win32Exception from native GetLastError after ExpandEnvironm
Error message
Win32Exception from native GetLastError after ExpandEnvironmentStrings returned 0 (first call)
What it means
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.
Source
Thrown at contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java:1236
* name. If the name is not found, the %variableName% portion
* is left unexpanded.</p>
*
* <p>Note that this function does not support all the features
* that Cmd.exe supports. For example, it does not support
* %variableName:str1=str2% or %variableName:~offset,length%.</p>
*
* @return the replaced string
* @throws Win32Exception if an error occurs
*/
public static String expandEnvironmentStrings(String input) {
if(input == null) {
return "";
}
int resultChars = Kernel32.INSTANCE.ExpandEnvironmentStrings(input, null, 0);
if(resultChars == 0) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
Memory resultMemory;
if( W32APITypeMapper.DEFAULT == W32APITypeMapper.UNICODE ) {
resultMemory = new Memory(resultChars * Native.WCHAR_SIZE);
} else {
// return value is length in chars including terminating NULL,
// documentation for ANSI version says: buffer size should be the
// string length, plus terminating null character, plus one
resultMemory = new Memory(resultChars + 1);
}
resultChars = Kernel32.INSTANCE.ExpandEnvironmentStrings(input, resultMemory, resultChars);
if(resultChars == 0) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
if( W32APITypeMapper.DEFAULT == W32APITypeMapper.UNICODE ) {View on GitHub (pinned to d036ad9781)
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.
Example fix
// before
String expanded = Kernel32Util.expandEnvironmentStrings(userSuppliedPath);
// after
String expanded;
try {
expanded = Kernel32Util.expandEnvironmentStrings(userSuppliedPath);
} catch (Win32Exception e) {
LOG.warn("Environment expansion failed with Win32 error " + e.getErrorCode() + ", using raw path");
expanded = userSuppliedPath;
} Defensive patterns
Strategy: try-catch
Validate before calling
if (input == null || input.isEmpty()) {
throw new IllegalArgumentException("expandEnvironmentStrings requires a non-empty input");
}
if (!Platform.isWindows()) {
throw new IllegalStateException("expandEnvironmentStrings is Windows-only");
} Type guard
boolean isExpandableInput(String s) { return s != null && !s.isEmpty(); } Try / catch
try {
result = Kernel32Util.expandEnvironmentStrings(input);
} catch (Win32Exception e) {
log.warn("ExpandEnvironmentStrings failed, Win32 code {}", e.getErrorCode());
result = input; // fall back to unexpanded
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Win32Exception from native GetLastError when GetThreadPriori
- Win32Exception from native GetLastError after SetThreadPrior
- 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/91cb3eca97ea4605.
Report an issue: GitHub.