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

  1. Inspect Win32Exception.getErrorCode() and cross-reference it with the Win32 error list to find the native cause.
  2. Verify the input string is a valid, non-corrupt path/string with %VAR% references that ExpandEnvironmentStrings can process.
  3. Confirm the code runs on Windows with a functioning kernel32 and JNA native access (no security policy blocking native calls).
  4. 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

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


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