{"record":{"id":"9c8080dfcd3bd1ed","repo":"theonedev/onedev","slug":"path-does-not-represent-a-tree-treewalk-getpa","errorCode":null,"errorMessage":"Path does not represent a tree: \" + treeWalk.getPathString()","messagePattern":"Path does not represent a tree: \" \\+ treeWalk\\.getPathString\\(\\)","errorType":"exception","errorClass":"NotTreeException","httpStatus":null,"severity":"error","filePath":"server-core/src/main/java/io/onedev/server/git/service/DefaultGitService.java","lineNumber":739,"sourceCode":"\t\t\t\t\t\t\t\t it.hasNext(); ) {\n\t\t\t\t\t\t\t\tMap.Entry<String, BlobContent> entry = it.next();\n\t\t\t\t\t\t\t\tif (entry.getKey().startsWith(name + \"/\")) {\n\t\t\t\t\t\t\t\t\tchildNewBlobs.put(entry.getKey().substring(name.length() + 1), entry.getValue());\n\t\t\t\t\t\t\t\t\tit.remove();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!childOldPaths.isEmpty() || !childNewBlobs.isEmpty()) {\n\t\t\t\t\t\t\t\tif ((treeWalk.getFileMode(0).getBits() & FileMode.TYPE_TREE) != 0) {\n\t\t\t\t\t\t\t\t\tTreeWalk childTreeWalk = TreeWalk.forPath(treeWalk.getObjectReader(), treeWalk.getPathString(),\n\t\t\t\t\t\t\t\t\t\t\trevTree);\n\t\t\t\t\t\t\t\t\tPreconditions.checkNotNull(childTreeWalk);\n\t\t\t\t\t\t\t\t\tchildTreeWalk.enterSubtree();\n\t\t\t\t\t\t\t\t\tObjectId childTreeId = insertTree(revTree, childTreeWalk, inserter, treeWalk.getPathString(),\n\t\t\t\t\t\t\t\t\t\t\tchildOldPaths, childNewBlobs);\n\t\t\t\t\t\t\t\t\tif (childTreeId != null)\n\t\t\t\t\t\t\t\t\t\tentries.add(new TreeFormatterEntry(name, FileMode.TREE.getBits(), childTreeId));\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tthrow new NotTreeException(\"Path does not represent a tree: \" + treeWalk.getPathString());\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tentries.add(new TreeFormatterEntry(name, treeWalk.getFileMode(0).getBits(), treeWalk.getObjectId(0)));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!currentOldPaths.isEmpty()) {\n\t\t\t\t\t\tString nonExistPath = currentOldPaths.iterator().next();\n\t\t\t\t\t\tif (parentPath != null)\n\t\t\t\t\t\t\tnonExistPath = parentPath + \"/\" + nonExistPath;\n\t\t\t\t\t\tthrow new NotFoundException(\"Unable to find path \" + nonExistPath);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!currentNewBlobs.isEmpty()) {\n\t\t\t\t\t\tSet<String> files = new HashSet<>();\n\t\t\t\t\t\tfor (Map.Entry<String, BlobContent> entry : currentNewBlobs.entrySet()) {\n\t\t\t\t\t\t\tString path = entry.getKey();","sourceCodeStart":721,"sourceCodeEnd":757,"githubUrl":"https://github.com/theonedev/onedev/blob/d44925c47c37992c828ea673a5f9620539bc3ff2/server-core/src/main/java/io/onedev/server/git/service/DefaultGitService.java#L721-L757","documentation":"While inserting trees in DefaultGitService, the code expects the old-tree entry at a needed path segment to be a directory so it can recurse (insertTree). If the entry exists but is not a tree (e.g. a blob), it throws NotTreeException(\"Path does not represent a tree: <path>\") — a new blob is being placed under a path that currently exists as a file, so the subtree cannot be created.","triggerScenarios":"Committing/adding a file like 'assets/logo.png' when the existing tree already contains a blob named 'assets' (a file, not a directory); the tree walk hits the blob where a subtree should be entered and insertTree's child-walk is impossible.","commonSituations":"Migrations/imports that add nested paths over a flat file layout; restoring snapshots with changed file/folder roles; case-insensitive filesystems causing a file 'Assets' to collide with dir 'assets/'.","solutions":["First delete the existing blob at that path (commit removing it), then add the nested files in a follow-up change","Rename the new nested path (or the existing file) to avoid the file-vs-directory conflict","Pre-check the target tree: resolve the parent path and verify it is a tree before applying changes"],"exampleFix":"// before\n// existing blob \"docs\", adding \"docs/index.md\"\nchanges.add(new FileChange(FileOperation.ADD, \"docs/index.md\", mdBlobId));\n// after\nchanges.add(new FileChange(FileOperation.DELETE, \"docs\", oldBlobId));\nchanges.add(new FileChange(FileOperation.ADD, \"docs/index.md\", mdBlobId));","handlingStrategy":"validation","validationCode":"// resolve parent path in the old tree; must be a tree to add children\nObjectId parent = TreeWalk.forPath(repo, parentPath, revTree) != null\n    ? /* check its FileMode */ : null;\nif (parent != null && !isTree(parent))\n  throw new IllegalArgumentException(\"Existing file blocks nested path: \" + parentPath);","typeGuard":"boolean isTreeEntry(FileMode mode) {\n  return (mode.getBits() & FileMode.TYPE_MASK) == FileMode.TYPE_TREE;\n}","tryCatchPattern":"try {\n  gitService.addFilesToTree(...);\n} catch (NotTreeException e) {\n  // path in message exists as a blob: delete it first or choose another path\n}","preventionTips":["Verify parent segments of any new nested path are directories in the target tree","Delete conflicting flat files before adding directory hierarchies","Beware case-only collisions when clients run on case-insensitive filesystems"],"tags":["git","tree-conflict","path-collision"],"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"}