java-native-access/jna · error · IOException

err

err

Error message

ReadDirectoryChangesW failed on <file>: '<message>' (<err>)

What it means

The background watcher thread of W32FileMonitor calls ReadDirectoryChangesW to register/change-buffer reads on the watched directory. When the native call fails (returns FALSE) while the monitor is not shutting down, an IOException with the formatted Win32 error message and code is thrown, aborting directory change monitoring for that file.

Solutions

  1. Catch the IOException from the monitor and re-register the watch once the directory is recreated/reconnected
  2. Verify the watched directory exists and is accessible before and during monitoring
  3. Use a local (non-network) directory when possible, as SMB shares commonly fail mid-watch
  4. Stop the monitor explicitly when disposing to avoid spurious errors from the race with disposal

Example fix

// before
W32FileMonitor monitor = new W32FileMonitor();
monitor.addWatch(dir, listener);
// after
W32FileMonitor monitor = new W32FileMonitor();
try {
    monitor.addWatch(dir, listener);
} catch (IOException e) {
    // directory vanished or was denied; retry after ensuring it exists
    if (dir.mkdirs()) monitor.addWatch(dir, listener);
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!dir.exists() || !dir.isDirectory()) {
    throw new IllegalStateException("Watch target missing: " + dir);
}

Try / catch

try {
    monitor.addWatch(dir, listener);
} catch (IOException e) {
    // parse Win32 code from message, re-register or degrade to polling
}

Prevention

When it happens

Trigger: The monitored directory handle becomes invalid (directory deleted/renamed, network share disconnected) while the monitor thread is running; the ReadDirectoryChangesW call inside handleChanges fails with a Win32 error such as ERROR_ACCESS_DENIED (5).

Common situations: Watching a directory on a removable or network drive that gets disconnected; the watched folder is deleted while the monitor is active; permission changes on the folder mid-watch.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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

Appendix: source

Thrown at contrib/platform/src/com/sun/jna/platform/win32/W32FileMonitor.java:118

            if (event != null) {
                notify(event);
            }

            fni = fni.next();
        } while (fni != null);

        // trigger the next read
        if (!finfo.file.exists()) {
            unwatch(finfo.file);
            return;
        }

        if (!klib.ReadDirectoryChangesW(finfo.handle, finfo.info,
                finfo.info.size(), finfo.recursive, finfo.notifyMask,
                finfo.infoLength, finfo.overlapped, null)) {
            if (!disposing) {
                int err = klib.GetLastError();
                throw new IOException("ReadDirectoryChangesW failed on "
                        + finfo.file + ": '"
                        + Kernel32Util.formatMessageFromLastErrorCode(err)
                        + "' (" + err + ")");
            }
        }
    }

    private FileInfo waitForChange() {
        IntByReference rcount = new IntByReference();
        ULONG_PTRByReference rkey = new ULONG_PTRByReference();
        PointerByReference roverlap = new PointerByReference();
        if (! Kernel32.INSTANCE.GetQueuedCompletionStatus(port, rcount, rkey, roverlap, WinBase.INFINITE)) {
            return null;
        }
        synchronized (this) {
            return handleMap.get(new HANDLE(rkey.getValue().toPointer()));
        }
    }

View on GitHub (pinned to d036ad9781)