quarkusio/quarkus · error · UncheckedIOException

Unable to scan: ${next}

Error message

Unable to scan: ${next}

What it means

WatchServiceFileSystemWatcher.doScan walks registered directories to snapshot last-modified times; if Files.list or getLastModifiedTime throws IOException for any path, it is rethrown as UncheckedIOException 'Unable to scan: <path>'. It means the dev-mode change scanner cannot read a directory it was asked to watch.

Source

Thrown at core/deployment/src/main/java/io/quarkus/deployment/dev/filesystem/watch/WatchServiceFileSystemWatcher.java:250

            watchService.close();
        }
    }

    private static Map<Path, Long> doScan(Path directory) {
        final Map<Path, Long> results = new HashMap<>();

        final Deque<Path> toScan = new ArrayDeque<>();
        toScan.add(directory);
        while (!toScan.isEmpty()) {
            Path next = toScan.pop();
            if (Files.isDirectory(next)) {
                try {
                    results.put(next, Files.getLastModifiedTime(directory).toMillis());
                    try (Stream<Path> list = Files.list(next)) {
                        list.forEach(p -> toScan.push(p.toAbsolutePath()));
                    }
                } catch (IOException e) {
                    throw new UncheckedIOException("Unable to scan: " + next, e);
                }
            }
        }
        return results;
    }

    private static void invokeCallback(FileChangeCallback callback, List<FileChangeEvent> results) {
        try {
            callback.handleChanges(results);
        } catch (Exception e) {
            log.error("Failed to invoke watch callback", e);
        }
    }

    private class PathData {

        private final Path path;
        private final List<FileChangeCallback> callbacks = new ArrayList<>();

View on GitHub (pinned to e1c734241f)

Solutions

  1. Check the path named in the message: restore it, remove broken symlinks, or fix permissions
  2. Run git clean/reset or restart quarkus:dev after mass file operations like branch switches
  3. Raise the open-file limit (ulimit -n) if 'Too many open files' appears as the cause
  4. Exclude volatile paths (node_modules, generated dirs) from watching if applicable

Example fix

// before: branch switch removes watched dir, scan fails
// after: restart dev mode after large checkouts, or fix path
rm -rf node_modules && npm ci  # recreate missing/legacy paths
ulimit -n 4096
Defensive patterns

Strategy: validation

Validate before calling

for (Path dir : watchedDirs) {
    if (!Files.isReadable(dir)) throw new IllegalStateException("Watched dir not readable: " + dir);
    if (Files.isSymbolicLink(dir) && !Files.exists(dir.toRealPath())) throw new IllegalStateException("Broken symlink: " + dir);
}

Try / catch

try {
    watcher.scan();
} catch (UncheckedIOException e) {
    if (e.getMessage().startsWith("Unable to scan:")) {
        Path bad = Path.of(e.getMessage().replace("Unable to scan: ", ""));
        // re-register or skip the missing directory, then rescan
    }
    throw e;
}

Prevention

When it happens

Trigger: allDirectories() -> doScan() iterating watched dirs when a directory disappears (deleted during scan), permission is denied, or a symlink is broken.

Common situations: A watched directory deleted while dev mode runs (clean, git checkout/branch switch); permission changes; too many open files (ulimit) breaking Files.list; broken symlinks in the project tree.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/037c270eabe12677. Report an issue: GitHub.