{"record":{"id":"54c0bb516ea86944","repo":"quarkusio/quarkus","slug":"symbolic-links-are-not-allowed-in-remote-dev-paths","errorCode":null,"errorMessage":"Symbolic links are not allowed in remote-dev paths: <file>","messagePattern":"Symbolic links are not allowed in remote-dev paths: <file>","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java","lineNumber":482,"sourceCode":"                || file.length() >= 2 && file.charAt(1) == ':') {\n            throw new IllegalArgumentException(\"Path is not below the application root: \" + file);\n        }\n        validateExistingPathComponents(normalizedRoot, resolved, file);\n        return resolved;\n    }\n\n    private static void validateExistingPathComponents(Path normalizedRoot, Path resolved, String file) {\n        final Path realRoot;\n        try {\n            realRoot = normalizedRoot.toRealPath();\n        } catch (IOException e) {\n            throw new IllegalArgumentException(\"Unable to validate the application root for remote-dev path: \" + file, e);\n        }\n        Path current = normalizedRoot;\n        for (Path element : normalizedRoot.relativize(resolved)) {\n            current = current.resolve(element);\n            if (Files.isSymbolicLink(current)) {\n                throw new IllegalArgumentException(\"Symbolic links are not allowed in remote-dev paths: \" + file);\n            }\n            if (Files.exists(current, LinkOption.NOFOLLOW_LINKS)) {\n                try {\n                    if (!current.toRealPath().startsWith(realRoot)) {\n                        throw new IllegalArgumentException(\"Path leaves the application root: \" + file);\n                    }\n                } catch (IOException e) {\n                    throw new IllegalArgumentException(\"Unable to validate remote-dev path: \" + file, e);\n                }\n            } else {\n                break;\n            }\n        }\n    }\n\n    private static String normalizeFile(String file) {\n        requireNonNull(file, \"file\");\n        file = file.replace('\\\\', '/');","sourceCodeStart":464,"sourceCodeEnd":500,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/core/deployment/src/main/java/io/quarkus/deployment/dev/RuntimeUpdatesProcessor.java#L464-L500","documentation":"For each path component of the resolved remote-dev file, validateExistingPathComponents rejects the path if any element is a symbolic link. Symbolic links could redirect writes outside the application root, so remote-dev sync refuses them outright with this IllegalArgumentException.","triggerScenarios":"updateFile or resolveApplicationPath encountering any symlink component under the application root (e.g. target/classes is a symlink, or the file itself is a symlink).","commonSituations":"Projects using symlinked source/output dirs (common with some build setups or Dropbox-synced folders); macOS /tmp symlink when running from /tmp; container image layouts using symlinks.","solutions":["Replace symlinked directories under the application root with real directories (cp -rL, then delete the link).","Run the dev server from a real path (use the resolved path, e.g. /private/var/... instead of /var/... on macOS).","Configure your build to output classes to a non-symlinked directory.","If a symlink is unavoidable, use local dev mode instead of remote-dev sync for that layout."],"exampleFix":"# before: target/classes -> /mnt/shared/classes (symlink)\n# after\ncp -rL target/classes target/classes.real && rm target/classes && mv target/classes.real target/classes","handlingStrategy":"validation","validationCode":"Path root = Path.of(\".\").toAbsolutePath().normalize();\nPath target = root.resolve(relPath).normalize();\nfor (Path cur = root; cur.getNameCount() <= target.getNameCount(); ) {\n    if (Files.isSymbolicLink(cur)) throw new IllegalStateException(\"symlink not allowed: \" + cur);\n    if (cur.equals(target)) break;\n    cur = cur.resolve(target.getName(root.relativize(cur).getNameCount()));\n}","typeGuard":"boolean hasNoSymlinks(Path root, Path target) throws IOException {\n    Path cur = root;\n    for (Path el : root.relativize(target.normalize())) {\n        cur = cur.resolve(el);\n        if (Files.isSymbolicLink(cur)) return false;\n    }\n    return true;\n}","tryCatchPattern":"try {\n    client.sync(path);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().startsWith(\"Symbolic links are not allowed\")) {\n        log.errorf(\"%s contains a symlink — replace with a real directory\", path);\n    } else throw e;\n}","preventionTips":["Replace symlinked build-output directories with real ones","On macOS use /private/... real paths instead of /tmp or /var symlinks","Audit build config for symlinked output dirs before enabling remote-dev"],"tags":["symlink","security","remote-dev","validation"],"backgroundTag":"symlink-not-allowed","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}