java-native-access/jna · error · Win32Exception

Win32Exception from native GetLastError after OpenThread fai

Error message

Win32Exception from native GetLastError after OpenThread failed

What it means

Kernel32Util.getThreadPriority(tid) calls OpenThread with THREAD_QUERY_INFORMATION; a NULL return is converted into a Win32Exception carrying native GetLastError(). Typical causes are a nonexistent thread id, the thread already exited, or insufficient access rights to the owning process's threads.

Source

Thrown at contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java:1411

                throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
            }
        } catch (final Win32Exception e) {
            we = e;
        } finally {
            cleanUp(hProcess, we);
        }
    }

    /**
     * Gets the priority value of the specified thread.
     *
     * @param tid Identifier for the running thread.
     * @throws Win32Exception if an error occurs.
     */
    public static int getThreadPriority(final int tid) {
        final HANDLE hThread = Kernel32.INSTANCE.OpenThread(WinNT.THREAD_QUERY_INFORMATION, false, tid);
        if (hThread == null) {
            throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
        }

        Win32Exception we = null;
        try {
            final int nPriority = Kernel32.INSTANCE.GetThreadPriority(hThread);
            if (!isValidThreadPriority(nPriority)) {
                throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
            }
            return nPriority;
        } catch (final Win32Exception e) {
            throw we = e; // re-throw to avoid return value!
        } finally {
            cleanUp(hThread, we);
        }
    }

    /**
     * Sets the priority value for the specified thread.

View on GitHub (pinned to d036ad9781)

Solutions

  1. Verify the tid is current (thread still running) before querying; drop ids collected earlier without refresh.
  2. Catch Win32Exception and check the code: 87 (ERROR_INVALID_PARAMETER) usually a bad/nonexistent tid, 5 (ERROR_ACCESS_DENIED) a permissions issue.
  3. Run elevated or use THREAD_QUERY_LIMITED_INFORMATION via Kernel32.INSTANCE.OpenThread directly when full query rights are denied.
  4. Ensure you are passing a thread id (from Win32 thread enumeration), not a process id.
  5. Re-enumerate threads each sampling cycle instead of caching tids across long intervals.

Example fix

// before
int prio = Kernel32Util.getThreadPriority(tid);
// after
try {
    int prio = Kernel32Util.getThreadPriority(tid);
} catch (Win32Exception e) {
    LOGGER.debug("Cannot query thread " + tid + " (may have exited): " + e.getMessage());
}
Defensive patterns

Strategy: try-catch

Validate before calling

// verify the tid is currently openable before querying
HANDLE h = Kernel32.INSTANCE.OpenThread(WinNT.THREAD_QUERY_LIMITED_INFORMATION, false, tid);
if (h == null) throw new IllegalStateException("tid " + tid + " not accessible (err " + Kernel32.INSTANCE.GetLastError() + ")");
Kernel32.INSTANCE.CloseHandle(h);

Type guard

boolean canQueryThread(int tid) {
    HANDLE h = Kernel32.INSTANCE.OpenThread(WinNT.THREAD_QUERY_LIMITED_INFORMATION, false, tid);
    if (h != null) Kernel32.INSTANCE.CloseHandle(h);
    return h != null;
}

Try / catch

try {
    int prio = Kernel32Util.getThreadPriority(tid);
} catch (Win32Exception e) {
    int code = e.getHR().intValue() & 0xFFFF; // 5=access denied, 87=bad/stale tid
    LOGGER.debug("getThreadPriority(" + tid + ") failed, win32 code " + code);
    return UNAVAILABLE;
}

Prevention

When it happens

Trigger: Calling Kernel32Util.getThreadPriority(tid) when OpenThread fails: stale tid after thread termination, tid from another process without access rights, or tid typos/incorrect id source.

Common situations: Sampling thread priorities of foreign processes from a non-elevated JVM; iterating cached thread ids while threads exit; confusing pids with tids and passing a process id instead.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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