{"record":{"id":"ebe5e86a30f1a73f","repo":"twpayne/chezmoi","slug":"s-not-a-directory-ebe5e8","errorCode":null,"errorMessage":"%s: not a directory","messagePattern":"(.+?): not a directory","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/chezmoi/sourcestatetreenode.go","lineNumber":137,"sourceCode":"\t\t\tdirAttr := DirAttr{\n\t\t\t\tTargetName: componentRelPath.String(),\n\t\t\t}\n\t\t\ttargetStateDir := &TargetStateDir{\n\t\t\t\tperm: dirAttr.perm() &^ umask,\n\t\t\t}\n\t\t\tsourceRelPath = sourceRelPath.Join(NewSourceRelPath(dirAttr.SourceName()))\n\t\t\tsourceStateDir = &SourceStateDir{\n\t\t\t\tattr:             dirAttr,\n\t\t\t\torigin:           origin,\n\t\t\t\tsourceRelPath:    sourceRelPath,\n\t\t\t\ttargetStateEntry: targetStateDir,\n\t\t\t}\n\t\t\tnode.SourceStateEntry = sourceStateDir\n\t\t} else {\n\t\t\tvar ok bool\n\t\t\tsourceStateDir, ok = node.SourceStateEntry.(*SourceStateDir)\n\t\t\tif !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"%s: not a directory\", componentRelPaths[0].Join(componentRelPaths[1:i+1]...))\n\t\t\t}\n\t\t\tsourceRelPath = sourceRelPath.Join(NewSourceRelPath(sourceStateDir.attr.SourceName()))\n\t\t}\n\t}\n\treturn sourceStateDir, nil\n}\n\n// Set sets the SourceStateEntry at relPath to sourceStateEntry.\nfunc (n *SourceStateEntryTreeNode) Set(targetRelPath RelPath, sourceStateEntry SourceStateEntry) {\n\tif targetRelPath.IsEmpty() {\n\t\tn.SourceStateEntry = sourceStateEntry\n\t\treturn\n\t}\n\n\tnode := n\n\tfor _, childRelPath := range targetRelPath.SplitAll() {\n\t\tif node.Children == nil {\n\t\t\tnode.Children = make(map[RelPath]*SourceStateEntryTreeNode)","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/twpayne/chezmoi/blob/f901167e4685db90da56d6a2a19df642cb3e0247/internal/chezmoi/sourcestatetreenode.go#L119-L155","documentation":"While building the source-state tree node-by-node, chezmoi expects each intermediate component to be a SourceStateDir. If an existing entry at that path is not a directory (e.g. a file where a directory is needed), it errors '<path>: not a directory'.","triggerScenarios":"Source state contains a regular file/symlink at a path that must be traversed as a directory, e.g. adding target a/b/c when source already has a file named a or a/b; name collision between a file and required parent directory.","commonSituations":"A file and directory target the same nested path (e.g. ~/.config exists as a file); rename from dir to file (or vice versa) in the source repo without cleaning up; mis-typed target paths causing a component clash.","solutions":["Remove or rename the conflicting non-directory entry at that path in the source state (chezmoi source path)","Decide the correct shape: if it should be a directory, delete the stale file (chezchoi managed remove) and re-add entries","Run `chezmoi doctor` / inspect the source directory to find the file occupying the component path"],"exampleFix":"// before: source has file 'dot_config' and you add dot_config/nvim/init.lua\nrm ~/.local/share/chezmoi/dot_config\n// after: recreate as directory then add entries\nchezmoi add ~/.config/nvim/init.lua","handlingStrategy":"validation","validationCode":"// before adding nested target a/b/c, ensure source components can be directories\n// (no file already occupies a or a/b in the source state)\nfor _, p := range []string{\"dot_config\", \"dot_config/nvim\"} {\n  fi, err := os.Lstat(filepath.Join(sourceDir, p))\n  if err == nil && !fi.IsDir() {\n    return fmt.Errorf(\"source path %s is a file but must be a directory\", p)\n  }\n}","typeGuard":"func isDirAt(root, rel string) bool {\n  fi, err := os.Stat(filepath.Join(root, rel))\n  return err == nil && fi.IsDir()\n}","tryCatchPattern":"if err := apply(); err != nil {\n  if strings.HasSuffix(err.Error(), \": not a directory\") {\n    return fmt.Errorf(\"file/dir conflict in source state; inspect source tree: %w\", err)\n  }\n  return err\n}","preventionTips":["Avoid managing both a file and children under the same target path","After renames dir<->file, clean stale entries from the chezmoi source directory","Inspect the source tree (chezmoi source-path) when nesting changes"],"tags":["chezmoi","source-state","filesystem"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"f901167e4685db90da56d6a2a19df642cb3e0247","analyzedAt":"2026-09-01T18:16:41.508Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T01:17:15.007Z"}