{"record":{"id":"c7f342c27b7c6108","repo":"wagoodman/dive","slug":"cannot-add-relative-path-s","errorCode":null,"errorMessage":"cannot add relative path '%s'","messagePattern":"cannot add relative path '(.+?)'","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"dive/filetree/file_tree.go","lineNumber":247,"sourceCode":"\tnodeNames := strings.Split(strings.Trim(path, \"/\"), \"/\")\n\tnode := tree.Root\n\tfor _, name := range nodeNames {\n\t\tif name == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif node.Children[name] == nil {\n\t\t\treturn nil, fmt.Errorf(\"path does not exist: %s\", path)\n\t\t}\n\t\tnode = node.Children[name]\n\t}\n\treturn node, nil\n}\n\n// AddPath adds a new node to the tree with the given payload\nfunc (tree *FileTree) AddPath(filepath string, data FileInfo) (*FileNode, []*FileNode, error) {\n\tfilepath = path.Clean(filepath)\n\tif filepath == \".\" {\n\t\treturn nil, nil, fmt.Errorf(\"cannot add relative path '%s'\", filepath)\n\t}\n\tnodeNames := strings.Split(strings.Trim(filepath, \"/\"), \"/\")\n\tnode := tree.Root\n\taddedNodes := make([]*FileNode, 0)\n\tfor idx, name := range nodeNames {\n\t\tif name == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\t// find or create node\n\t\tif node.Children[name] != nil {\n\t\t\tnode = node.Children[name]\n\t\t} else {\n\t\t\t// don't add paths that should be deleted\n\t\t\tif strings.HasPrefix(name, doubleWhiteoutPrefix) {\n\t\t\t\treturn nil, addedNodes, nil\n\t\t\t}\n\n\t\t\t// don't attach the payload. The payload is destined for the","sourceCodeStart":229,"sourceCodeEnd":265,"githubUrl":"https://github.com/wagoodman/dive/blob/d6c691947f8fda635c952a17ee3b7555379d58f0/dive/filetree/file_tree.go#L229-L265","documentation":"Returned by FileTree.AddPath (dive/filetree/file_tree.go:247). AddPath runs path.Clean on the input and rejects the result '.', which is what Clean produces for empty strings, '.', './' and repeated slashes. The tree is rooted; you must add absolute-style paths.","triggerScenarios":"Calling AddPath(\"\", \".\", or \"./\") - anything that cleans to the root itself; also a computed relative filename from a tar header or walk that is empty after trimming slashes.","commonSituations":"Feeding tar entries with degenerate names (\"./\" entries some builders emit), or passing a relative path where an absolute one was expected when building trees programmatically.","solutions":["Pass clean absolute-style paths: strings like \"/app/bin/tool\", not \"./app/bin/tool\" or \"\"","Filter degenerate tar/walk entries before building the tree: if path.Clean(name) == \".\" { continue }","At the call site, keep using filepath.Join(\"/\", name) to force an absolute form for relative inputs"],"exampleFix":"// before\n_, _, err := tree.AddPath(relName, info) // relName == \"./\" or \"\" from a tar header\n\n// after\nif cleaned := path.Clean(relName); cleaned == \".\" {\n    continue // skip root-alias entries\n}\ntree.AddPath(\"/\"+strings.TrimPrefix(cleaned, \"/\"), info)","handlingStrategy":"validation","validationCode":"// reject/normalize degenerate inputs before AddPath\ncleaned := path.Clean(p)\nif cleaned == \".\" {\n    return nil // or: skip when filtering tar entries\n}\ncleaned = \"/\" + strings.TrimPrefix(cleaned, \"/\")\nnode, added, err := tree.AddPath(cleaned, data)","typeGuard":null,"tryCatchPattern":"node, added, err := tree.AddPath(p, data)\nif err != nil && strings.Contains(err.Error(), \"cannot add relative path\") {\n    // normalize and retry once with an absolute path\n    node, added, err = tree.AddPath(\"/\"+strings.TrimPrefix(path.Clean(p), \"/\"), data)\n}","preventionTips":["Always build paths as absolute ('/a/b') from the start","Filter tar/walk entries whose cleaned name is '.' or empty","Wrap tree construction in a helper that normalizes once"],"tags":["filetree","path-validation","tar","api-contract"],"backgroundTag":null,"analyzedSha":"d6c691947f8fda635c952a17ee3b7555379d58f0","analyzedAt":"2026-08-15T09:42:35.293Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}