java-native-access/jna · error · IllegalStateException
Mismatched allocated (X) vs. received devices count (Y)
Error message
Mismatched allocated (X) vs. received devices count (Y)
What it means
GetRawInputDeviceList was called with a pre-allocated RAWINPUTDEVICELIST array whose size was obtained via a first (count-query) call. If the actual number of devices returned differs from the array length, JNA throws this IllegalStateException because the buffer is inconsistent with the returned count, and returning partial/garbage records would be unsafe.
Source
Thrown at contrib/platform/src/com/sun/jna/platform/win32/User32Util.java:109
public static final List<RAWINPUTDEVICELIST> GetRawInputDeviceList() {
IntByReference puiNumDevices = new IntByReference(0);
RAWINPUTDEVICELIST placeholder = new RAWINPUTDEVICELIST();
int cbSize = placeholder.sizeof();
// first call is with NULL so we query the expected number of devices
int returnValue = User32.INSTANCE.GetRawInputDeviceList(null, puiNumDevices, cbSize);
if (returnValue != 0) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
int deviceCount = puiNumDevices.getValue();
RAWINPUTDEVICELIST[] records = (RAWINPUTDEVICELIST[]) placeholder.toArray(deviceCount);
returnValue = User32.INSTANCE.GetRawInputDeviceList(records, puiNumDevices, cbSize);
if (returnValue == (-1)) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
if (returnValue != records.length) {
throw new IllegalStateException("Mismatched allocated (" + records.length + ") vs. received devices count (" + returnValue + ")");
}
return Arrays.asList(records);
}
/**
* Helper class, that runs a windows message loop as a seperate thread.
*
* This is intended to be used in conjunction with APIs, that need a
* spinning message loop. One example for this are the DDE functions, that
* can only be used if a message loop is present.
*
* To enable interaction with the mainloop the MessageLoopThread allows to
* dispatch callables into the mainloop and let these Callables be invoked
* on the message thread.
*
* This implies, that the Callables should block the loop as short as possible.
*/View on GitHub (pinned to d036ad9781)
Solutions
- Catch the IllegalStateException and simply retry GetRawInputDeviceList — the second call re-queries the count and will usually succeed
- Call the method again inside a small retry loop (2-3 attempts) since the mismatch is inherently transient
- Avoid hot-plugging devices during enumeration in automated/test setups
Example fix
// before
List<RAWINPUTDEVICELIST> devices = User32Util.GetRawInputDeviceList();
// after
List<RAWINPUTDEVICELIST> devices;
try {
devices = User32Util.GetRawInputDeviceList();
} catch (IllegalStateException e) {
devices = User32Util.GetRawInputDeviceList(); // retry: device list changed mid-query
} Defensive patterns
Strategy: retry
Validate before calling
// No pre-validation possible; just wrap the call in a bounded retry
List<RAWINPUTDEVICELIST> list;
for (int i = 0; i < 3; i++) {
try { list = User32Util.GetRawInputDeviceList(); break; }
catch (IllegalStateException e) { if (i == 2) throw e; }
} Try / catch
try {
devices = User32Util.GetRawInputDeviceList();
} catch (IllegalStateException e) {
devices = User32Util.GetRawInputDeviceList(); // transient race
} Prevention
- Always wrap this call in a small retry loop — device counts can change between the two native calls
- Avoid enumerating during hot-plug-heavy scenarios (device driver installs)
- Log and retry rather than crashing; the mismatch is inherently transient
When it happens
Trigger: Calling User32Util.GetRawInputDeviceList when raw input devices are plugged in or removed between the count-query call and the array-allocation call, so GetRawInputDeviceList returns a count different from the previously queried number.
Common situations: Hot-plugging/unplugging HID devices (mouse, keyboard, gamepad) between the two native calls; concurrent processes enumerating devices; virtualized/RDP environments where device lists change dynamically.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- Win32Exception from native GetLastError after ExpandEnvironm
- LookupAccountNameW was expected to fail with ERROR_INSUFFICI
- Expected GetTokenInformation to fail with ERROR_INSUFFICIENT
- Failed to find privilege "{privilege}" - {GetLastError}
- Window width and/or height were 0 even though GetWindowRect
AI-assisted analysis of java-native-access/jna@d036ad9781 (2026-09-12).
Data as JSON: /api/errors/a69190704034c034.
Report an issue: GitHub.