{"record":{"id":"c247fef655c367b6","repo":"restic/restic","slug":"path-s-not-a-directory","errorCode":null,"errorMessage":"path %s: not a directory","messagePattern":"path (.+?): not a directory","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/data/tree.go","lineNumber":321,"sourceCode":"\t\tsubfolder = path.Join(subfolder, name)\n\t\ttree, err := LoadTree(ctx, repo, *id)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"path %s: %w\", subfolder, err)\n\t\t}\n\t\tfinder := NewTreeFinder(tree)\n\t\tnode, err := finder.Find(name)\n\t\tfinder.Close()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"path %s: %w\", subfolder, err)\n\t\t}\n\t\tif node == nil {\n\t\t\tif runtime.GOOS == \"windows\" && strings.Contains(dir, \"\\\\\") {\n\t\t\t\treturn nil, fmt.Errorf(\"path %s: not found; subfolder syntax currently requires forward slashes; check the output of `restic ls` for valid paths\", subfolder)\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"path %s: not found\", subfolder)\n\t\t}\n\t\tif node.Type != NodeTypeDir || node.Subtree == nil {\n\t\t\treturn nil, fmt.Errorf(\"path %s: not a directory\", subfolder)\n\t\t}\n\t\tid = node.Subtree\n\t}\n\treturn id, nil\n}\n\ntype peekableNodeIterator struct {\n\titer  func() (NodeOrError, bool)\n\tstop  func()\n\tvalue *Node\n}\n\nfunc newPeekableNodeIterator(tree TreeNodeIterator) (*peekableNodeIterator, error) {\n\titer, stop := iter.Pull(tree)\n\tit := &peekableNodeIterator{iter: iter, stop: stop}\n\terr := it.Next()\n\tif err != nil {\n\t\tit.Close()","sourceCodeStart":303,"sourceCodeEnd":339,"githubUrl":"https://github.com/restic/restic/blob/a80be1478a4c537f8396e0db2b05120aa78f11e0/internal/data/tree.go#L303-L339","documentation":"The subfolder descent can only walk through directories: after finding the component it checks node.Type == NodeTypeDir and node.Subtree != nil. If the component is a file (or a dir without a stored subtree), descent cannot continue and this error is returned.","triggerScenarios":"Resolving \"<snapshotID>:a/b/c/d\" where 'c' is a regular file, not a directory; or a dir node whose Subtree pointer is nil in the stored tree.","commonSituations":"Pointing a directory-style subfolder at a file path; treating a symlink to a directory as a traversable directory (its Type is symlink, not dir).","solutions":["End the subfolder at the file itself instead of appending more components","If a symlink was meant, resolve it to its real target directory in the snapshot","Run `restic ls <snapshotID> <path>` to confirm which components are directories"],"exampleFix":"# before\nrestic dump latest:docs/readme.txt/extra stdout\n\n# after\nrestic dump latest:docs/readme.txt stdout","handlingStrategy":"validation","validationCode":"// If you drive the tree walk yourself, check node type before descending:\nnode, err := finder.Find(name)\nif err == nil && node != nil && node.Type != data.NodeTypeDir {\n\t// this is the terminal file; stop appending further components\n}","typeGuard":"func isDirNode(n *data.Node) bool {\n\treturn n != nil && n.Type == data.NodeTypeDir && n.Subtree != nil\n}","tryCatchPattern":null,"preventionTips":["Treat files as leaves: never append path components after a filename","Use `restic ls <snap>:<dir>` to confirm which components are directories","Remember symlinks are NodeTypeSymlink, not traversable directories"],"tags":["restic","tree","subfolder","filesystem"],"backgroundTag":null,"analyzedSha":"a80be1478a4c537f8396e0db2b05120aa78f11e0","analyzedAt":"2026-08-15T15:30:29.928Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}