java-native-access/jna · error · RuntimeException

Failed to get a change handle

Error message

Failed to get a change handle - <errorCode>

What it means

monitorAllPrinters() calls FindFirstPrinterChangeNotification to obtain a change-notification handle for printer/job events. If the returned handle is not valid, the last error code is captured and a RuntimeException 'Failed to get a change handle - <errorCode>' is thrown. Without this handle the monitor cannot watch for printer changes.

Solutions

  1. Check errorCode: ERROR_ACCESS_DENIED (5) means run elevated or grant notification rights on the printer/server.
  2. Verify the print-server handle from OpenPrinter was valid before calling FindFirstPrinterChangeNotification.
  3. Validate the PRINTER_NOTIFY_OPTIONS fields/types match the loaded native structure (version/platform mismatches can corrupt the struct).
  4. Retry after confirming the spooler service is healthy; catch RuntimeException to avoid crashing the monitor loop.

Example fix

// before
HANDLE h = Winspool.INSTANCE.FindFirstPrinterChangeNotification(...);
// after
HANDLE h = Winspool.INSTANCE.FindFirstPrinterChangeNotification(...);
if (!isValidHandle(h)) {
    int err = Kernel32.INSTANCE.GetLastError();
    LOG.error("Change-handle registration failed (code " + err + "); check privileges/spooler");
    return;
}
Defensive patterns

Strategy: validation

Validate before calling

HANDLEByReference h = new HANDLEByReference();
if (!Winspool.INSTANCE.OpenPrinter(null, h, null)) {
    throw new IllegalStateException("Cannot open print server: "
        + Kernel32.INSTANCE.GetLastError());
}

Type guard

boolean isValidChangeHandle(HANDLE h) {
    return h != null && !h.equals(WinBase.INVALID_HANDLE_VALUE);
}

Prevention

When it happens

Trigger: FindFirstPrinterChangeNotification returning INVALID_HANDLE_VALUE due to an invalid print-server handle, insufficient privileges for change notification, or a malformed PRINTER_NOTIFY_OPTIONS structure.

Common situations: Server handle was already closed or was null because a previous OpenPrinter failed silently; running without privileges to register change notifications on the spooler.

Related errors


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

Appendix: source

Thrown at contrib/w32printing/src/com/sun/jna/platform/win32/Win32SpoolMonitor.java:131

            optionsType.Type = JOB_NOTIFY_TYPE;
            optionsType.setFields(new short[] {
                JOB_NOTIFY_FIELD_PRINTER_NAME,
                JOB_NOTIFY_FIELD_STATUS,
                JOB_NOTIFY_FIELD_DOCUMENT
            });
            optionsType.toArray(1);
            options.pTypes = optionsType;
            HANDLE changeNotificationsHandle =
                Winspool.INSTANCE.FindFirstPrinterChangeNotification(
                    printServerHandle.getValue(),
                    PRINTER_CHANGE_ADD_JOB |
                        PRINTER_CHANGE_SET_JOB |
                        PRINTER_CHANGE_DELETE_JOB,
                    TWO_DIMENSIONAL_PRINTERS,
                    options);
            if (!isValidHandle(changeNotificationsHandle)) {
                int errorCode = Kernel32.INSTANCE.GetLastError();
                throw new RuntimeException("Failed to get a change handle - " +
                    errorCode);
            }

            try {
                while (true) {
                    Kernel32.INSTANCE.WaitForSingleObject(
                        changeNotificationsHandle,
                        WinBase.INFINITE);

                    DWORDByReference change =
                        new DWORDByReference();
                    PointerByReference infoPointer = new PointerByReference();
                    success =
                        Winspool.INSTANCE.FindNextPrinterChangeNotification(
                            changeNotificationsHandle,
                            change,
                            options,
                            infoPointer);

View on GitHub (pinned to d036ad9781)