oracle/graal · error · IOException

parent directory doesn't exist for: {resourcePath}

Error message

parent directory doesn't exist for: {resourcePath}

What it means

WatcherThread.addWatch registers a filesystem watch for a file path but must watch its parent directory. Path.getParent() returns null when the path has no parent (a root like '/' or a relative single name), which the code reports as IOException('parent directory doesn't exist for: ...').

Source

Thrown at espresso/src/com.oracle.truffle.espresso.hotswap/src/com/oracle/truffle/espresso/hotswap/ServiceWatcher.java:279

        // Map all active file watches with the set of paths for which the watch was
        // registered to enable us to cancel watches when deleted folders are recreated.
        // A counter on each path reason is kept for managing when we should cancel the
        // file system watch on the registered path.
        private final Map<Path, Map<Path, Integer>> activeFileWatches = new HashMap<>();

        private WatcherThread() throws IOException {
            super("hotswap-watcher-1");
            this.setDaemon(true);
            this.watchService = FileSystems.getDefault().newWatchService();
        }

        public void addWatch(Path resourcePath, Runnable callback) throws IOException {
            watchActions.put(resourcePath, new ServiceWatcher.State(calculateChecksum(resourcePath), callback));
            // register watch on parent folder
            Path dir = resourcePath.getParent();
            if (dir == null) {
                throw new IOException("parent directory doesn't exist for: " + resourcePath);
            }
            registerFileSystemWatch(dir, resourcePath, ALL_WATCH_KINDS);
        }

        private void addWatchedDeletedFolder(Path path, Path leaf) {
            Set<Path> set = watchedForDeletion.get(path);
            if (set == null) {
                set = new HashSet<>();
                watchedForDeletion.put(path, set);
            }
            set.add(leaf);
        }

        private void addWatchedCreatedFolder(Path path, Path leaf) {
            Set<Path> set = watchedForCreation.get(path);
            if (set == null) {
                set = new HashSet<>();
                watchedForCreation.put(path, set);

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Pass an absolute, non-root file path (a real file inside a directory) to addWatch.
  2. Normalize with path.toAbsolutePath().normalize() before registering.
  3. Guard: if path.getParent() == null, skip or report instead of registering.

Example fix

// before
watcher.addWatch(Path.of("services.cfg")); // relative, parent may be null

// after
Path p = Path.of("services.cfg").toAbsolutePath().normalize();
if (p.getParent() != null) {
    watcher.addWatch(p);
}
Defensive patterns

Strategy: validation

Validate before calling

Path abs = path.toAbsolutePath().normalize();
if (abs.getParent() != null) {
    watcher.addWatch(abs, callback);
}

Prevention

When it happens

Trigger: addWatch called with a root path ('/') or a bare relative filename with no directory component while the service watcher tries to register the containing folder.

Common situations: Resource URLs resolving to filesystem roots; mis-resolved URL.toURI() producing root-level paths; relative paths used from an unexpected working directory.

Related errors


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/9373c15f904a3aef. Report an issue: GitHub.