{"record":{"id":"3cca0bee3c53ec0e","repo":"theonedev/onedev","slug":"path-treepath-does-not-exist-or-is-not-a-tree","errorCode":null,"errorMessage":"Path '${treePath}' does not exist or is not a tree.","messagePattern":"Path '(.+?)' does not exist or is not a tree\\.","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"server-core/src/main/java/org/eclipse/jgit/revwalk/LastCommitsOfChildren.java","lineNumber":91,"sourceCode":"\t\t\t@Nullable String treePath, @Nullable final Cache cache) {\n\t\ttry (RevWalk revWalk = new RevWalk(repo)) {\n\t\t\ttreePath = GitUtils.normalizePath(treePath);\n\t\t\tif (treePath == null) \n\t\t\t\ttreePath = \"\";\n\t\t\t\n\t\t\tfinal byte[] treePathRaw = Constants.encode(treePath);\n\t\t\tfinal Set<String> children = new HashSet<>();\n\t\t\tfinal Set<String> modifiedChildren = new HashSet<>();\n\n\t\t\tRevCommit untilCommit = revWalk.parseCommit(until);\n\n\t\t\t/*\n\t\t\t * Find out child directory or file names under the tree\n\t\t\t */\n\t\t\tif (treePath.length() != 0) {\n\t\t\t\tTreeWalk treeWalk = TreeWalk.forPath(repo, treePath, untilCommit.getTree());\n\t\t\t\tif (treeWalk == null || !FileMode.TREE.equals(treeWalk.getFileMode(0)))\n\t\t\t\t\tthrow new IllegalArgumentException(\"Path '\" + treePath + \"' does not exist or is not a tree.\");\n\t\t\t\ttreeWalk.enterSubtree();\n\t\t\t\ttreeWalk.setRecursive(false);\n\t\t\t\twhile (treeWalk.next())\n\t\t\t\t\tchildren.add(treeWalk.getPathString().substring(treePath.length()+1));\n\t\t\t} else {\n\t\t\t\ttry (TreeWalk treeWalk = new TreeWalk(repo)) {\n\t\t\t\t\ttreeWalk.addTree(untilCommit.getTree());\n\t\t\t\t\ttreeWalk.setRecursive(false);\n\t\t\t\t\twhile (treeWalk.next())\n\t\t\t\t\t\tchildren.add(treeWalk.getPathString().substring(treePath.length()));\n\t\t\t\t}\n\t\t\t}\n\t\t\t\n\t\t\trevWalk.markStart(untilCommit);\n\t\t\trevWalk.setRewriteParents(false);\n\n\t\t\t/* \n\t\t\t * Records last commits info of first encountered commit in cache, and we ","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/org/eclipse/jgit/revwalk/LastCommitsOfChildren.java#L73-L109","documentation":"LastCommitsOfChildren computes the last commit that touched each child of a directory in a git tree. Before walking, it resolves the given treePath against the 'until' commit's tree with TreeWalk.forPath; if the path does not exist, or exists but is a blob (file) rather than a directory, it throws this IllegalArgumentException. Only an existing directory (FileMode.TREE) can have children.","triggerScenarios":"Calling new LastCommitsOfChildren(repo, untilCommit, path) where path is empty-but-null-adjacent misspellings aside: (1) the path does not exist in the commit's tree (deleted/renamed/typo), (2) the path is a file, not a directory, (3) the path exists only in another branch/commit than untilCommit, (4) leading/trailing slashes or non-normalized paths that JGit cannot resolve.","commonSituations":"Rendering a file-browser 'last modified per entry' view in OneDev where a user-supplied path param points to a file or a stale path after a refactor; caching a directory path from an older commit and reusing it against a newer untilCommit where the directory was removed; building the path by string concatenation producing double slashes.","solutions":["Verify the path exists and is a directory in the same commit passed as untilCommit before constructing LastCommitsOfChildren (TreeWalk.forPath(repo, path, untilCommit.getTree()) != null and FileMode.TREE.equals(tw.getFileMode(0))).","Normalize the path: strip leading/trailing '/' and resolve '.'/'..' segments.","If the path is a file, request last-commit data for the file's parent directory instead, or use a file-oriented API.","Handle the case where the directory was deleted in untilCommit by falling back to the commit where it last existed.","Catch IllegalArgumentException as a signal to return 404/not-a-directory to the caller."],"exampleFix":"// before\nLastCommitsOfChildren children = new LastCommitsOfChildren(project.getRepository(), commit, path);\n// after\ntry (RevWalk rw = new RevWalk(project.getRepository())) {\n    RevCommit c = rw.parseCommit(commit.getId());\n    if (!path.isEmpty()) {\n        TreeWalk tw = TreeWalk.forPath(project.getRepository(), path, c.getTree());\n        if (tw == null || !FileMode.TREE.equals(tw.getFileMode(0)))\n            throw new NotFoundException(\"Path is not a directory in commit \" + c.getName());\n    }\n}\nLastCommitsOfChildren children = new LastCommitsOfChildren(project.getRepository(), commit, path);","handlingStrategy":"validation","validationCode":"boolean isDirectoryIn(Repository repo, RevCommit c, String path) throws IOException {\n    if (path == null || path.isEmpty()) return true;\n    String p = StringUtils.strip(path, \"/\");\n    try (TreeWalk tw = TreeWalk.forPath(repo, p, c.getTree())) {\n        return tw != null && FileMode.TREE.equals(tw.getFileMode(0));\n    }\n}","typeGuard":"boolean isTreeMode(TreeWalk tw) { return tw != null && FileMode.TREE.equals(tw.getFileMode(0)); }","tryCatchPattern":"try {\n    return new LastCommitsOfChildren(repo, commit, path);\n} catch (IllegalArgumentException e) {\n    throw new ResourceNotFoundException(\"Directory not found in commit: \" + path);\n}","preventionTips":["Resolve the path against the same commit you pass as untilCommit","Strip leading/trailing slashes and normalize '..' before use","Distinguish file vs directory paths in your UI/URL routing","Cache paths together with the commit id they were valid for"],"tags":["git","invalid-argument","path-not-a-tree"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"d44925c47c37992c828ea673a5f9620539bc3ff2","analyzedAt":"2026-09-06T07:18:27.995Z","contentChangedAt":"2026-09-06T07:18:27.995Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}