OtterMind/Chat2DB · error · IllegalArgumentException

Path is outside of the selected SQL directory

Error message

Path is outside of the selected SQL directory

What it means

Thrown by SqlDirectoryTreeStore.createChild() as a defense-in-depth containment guard: after resolving and normalizing the target path (parent.resolve(name).normalize()), it checks target.startsWith(root). This should never fire under normal operation because normalizeDirectoryName() already rejects names containing '/' or '\' and null bytes, preventing path traversal. If it fires, it indicates a bug in the name sanitization or an unexpected filesystem behavior.

Source

Thrown at chat2db-community-server/chat2db-community-jcef/src/main/java/ai/chat2db/community/jcef/handler/biz/SqlDirectoryTreeStore.java:125

    static Map<String, Object> createChild(String rootToken, String parentRelativePath, String rawName, String type)
            throws IOException {
        Path root = getRoot(rootToken);
        Path parent = resolveInRoot(root, parentRelativePath);
        if (!Files.isDirectory(parent, LinkOption.NOFOLLOW_LINKS)) {
            throw new IllegalArgumentException("Selected path is not a directory");
        }

        boolean directory = "directory".equals(type);
        boolean file = "file".equals(type);
        if (!directory && !file) {
            throw new IllegalArgumentException("Unsupported SQL directory child type");
        }

        String name = file ? normalizeFileName(rawName, "sql") : normalizeDirectoryName(rawName);
        Path target = parent.resolve(name).normalize();
        if (!target.startsWith(root)) {
            throw new IllegalArgumentException("Path is outside of the selected SQL directory");
        }
        if (Files.exists(target, LinkOption.NOFOLLOW_LINKS)) {
            throw new IllegalArgumentException("File or directory already exists");
        }

        if (directory) {
            Files.createDirectory(target);
        } else {
            Files.createFile(target);
        }

        Path realTarget = target.toRealPath(LinkOption.NOFOLLOW_LINKS);
        if (!realTarget.startsWith(root)) {
            throw new IllegalArgumentException("Path is outside of the selected SQL directory");
        }

        Map<String, Object> result = new HashMap<>();
        result.put("createdNode", toNode(rootToken, root, realTarget));

View on GitHub (pinned to 5ee1e990e7)

Solutions

  1. This is a security guard that should not fire — investigate why the sanitized name escaped root containment
  2. If on a case-insensitive filesystem, check whether root and target case alignment is the issue
  3. Review normalizeDirectoryName() for any path separator or traversal sequence it fails to reject
  4. Do not weaken or remove this guard — it is defense-in-depth against directory traversal
Defensive patterns

Strategy: validation

Validate before calling

// This guard is defense-in-depth and should not fire under normal operation.
// The only validation to add is ensuring normalizeDirectoryName has no gaps:
private static boolean isSafeName(String name) {
    return name != null && !name.isEmpty()
        && !name.contains("/") && !name.contains("\\")
        && !name.contains("..") && name.indexOf('\0') < 0;
}

Prevention

When it happens

Trigger: The normalized child target path does not start with the root path. Given that normalizeDirectoryName rejects path separators, this can only fire if the sanitization function has a regression, or if a filesystem-specific normalization (e.g., case-insensitive paths on Windows/macOS) causes startsWith to return false despite the path being logically contained.

Common situations: Extremely rare in practice. Could fire on case-insensitive filesystems (Windows, macOS default) if root and target differ in case after normalization; a future code change weakens normalizeDirectoryName; an OS path normalization quirk.

Related errors


AI-assisted analysis of OtterMind/Chat2DB@5ee1e990e7 (2026-08-14). Data as JSON: /api/errors/b8e1de5bcfedcbff. Report an issue: GitHub.