{"record":{"id":"a71f5d23aa862b8d","repo":"OtterMind/Chat2DB","slug":"selected-path-is-not-a-directory","errorCode":null,"errorMessage":"Selected path is not a directory","messagePattern":"Selected path is not a directory","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"warning","filePath":"chat2db-community-server/chat2db-community-jcef/src/main/java/ai/chat2db/community/jcef/handler/biz/SqlDirectoryTreeStore.java","lineNumber":44,"sourceCode":"    private static final Set<String> SUPPORTED_TEXT_EXTENSIONS = Set.of(\n            \"sql\", \"txt\", \"md\", \"markdown\",\n            \"json\", \"jsonl\", \"yaml\", \"yml\",\n            \"csv\", \"tsv\", \"xml\", \"log\",\n            \"env\", \"ini\", \"conf\", \"config\",\n            \"properties\", \"toml\");\n    private static final Set<String> SUPPORTED_IMAGE_EXTENSIONS = Set.of(\n            \"png\", \"jpg\", \"jpeg\", \"gif\", \"webp\", \"bmp\", \"ico\", \"avif\");\n    private static final Set<String> SUPPORTED_PREVIEW_EXTENSIONS = Set.of(\"pdf\");\n    private static final long MAX_PREVIEW_BYTES = 50L * 1024L * 1024L;\n    private static final ConcurrentMap<String, Path> ROOTS = new ConcurrentHashMap<>();\n\n    private SqlDirectoryTreeStore() {\n    }\n\n    static Map<String, Object> createRoot(String rootPath) throws IOException {\n        Path root = Paths.get(rootPath).toRealPath();\n        if (!Files.isDirectory(root, LinkOption.NOFOLLOW_LINKS)) {\n            throw new IllegalArgumentException(\"Selected path is not a directory\");\n        }\n\n        String rootToken = UUID.randomUUID().toString();\n        ROOTS.put(rootToken, root);\n\n        Map<String, Object> rootNode = new HashMap<>();\n        rootNode.put(\"key\", rootToken + \":\");\n        rootNode.put(\"rootToken\", rootToken);\n        rootNode.put(\"rootPath\", root.toString());\n        rootNode.put(\"name\", getDisplayName(root));\n        rootNode.put(\"path\", root.toString());\n        rootNode.put(\"relativePath\", \"\");\n        rootNode.put(\"type\", \"directory\");\n        rootNode.put(\"disabled\", false);\n        rootNode.put(\"sqlFile\", false);\n        rootNode.put(\"textFile\", false);\n        rootNode.put(\"previewFile\", false);\n        rootNode.put(\"hasChildren\", true);","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/OtterMind/Chat2DB/blob/5ee1e990e73fbcae1969dc554be254fedb3ab888/chat2db-community-server/chat2db-community-jcef/src/main/java/ai/chat2db/community/jcef/handler/biz/SqlDirectoryTreeStore.java#L26-L62","documentation":"Thrown by SqlDirectoryTreeStore.createRoot() when the user-selected path resolves (via toRealPath, which follows symlinks) to something that is not a directory. The method first calls Paths.get(rootPath).toRealPath() which would throw NoSuchFileException (IOException) for non-existent paths, so this IllegalArgumentException fires specifically when the path exists but is a regular file or special file — the user picked a file instead of a folder.","triggerScenarios":"Invoked via the open-sql-directory or select-sql-directory JCEF action when the 'path' field in the request JSON points to a file (e.g., a .sql file) rather than a directory. The filechooser or manual path entry selected a file.","commonSituations":"User picks a .sql file in the directory chooser instead of its parent folder; frontend sends a file path from a tree node click instead of a directory path; the path was correct at selection time but a file was created at that location by another process.","solutions":["In the frontend, ensure the directory picker (JFileChooser with DIRECTORIES_ONLY) or path input restricts selection to directories","Validate on the frontend that the selected path is a directory before sending the open-sql-directory request","If using a file chooser, set it to DIRECTORIES_ONLY mode so the user cannot select a file"],"exampleFix":"// before: file chooser allows file selection\nJFileChooser chooser = new JFileChooser();\nchooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);\n\n// after: restrict to directories only\nJFileChooser chooser = new JFileChooser();\nchooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);","handlingStrategy":"validation","validationCode":"// Before calling createRoot, validate the path is a directory\nPath candidate = Paths.get(rootPath);\nif (!Files.isDirectory(candidate)) {\n    throw new IllegalArgumentException(\"Path must be a directory: \" + rootPath);\n}\nSqlDirectoryTreeStore.createRoot(rootPath);","typeGuard":null,"tryCatchPattern":"try {\n    Map<String, Object> root = SqlDirectoryTreeStore.createRoot(path);\n    ResponseBuilder.buildSuccessJcef(Map.of(\"data\", root), callback);\n} catch (IllegalArgumentException e) {\n    callback.failure(400, e.getMessage()); // Use 400, not 500, for validation errors\n}","preventionTips":["Use JFileChooser with DIRECTORIES_ONLY mode so users cannot select files","Validate the path is a directory on the frontend before sending the request","Return HTTP 400 (not 500) for validation errors so the frontend can distinguish user errors from server errors"],"tags":["sql-directory","path-validation","user-input","filesystem"],"backgroundTag":null,"analyzedSha":"5ee1e990e73fbcae1969dc554be254fedb3ab888","analyzedAt":"2026-08-14T07:05:03.077Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}