twpayne/chezmoi · error

%s: parent directory not in source state

Error message

%s: parent directory not in source state

What it means

During 'chezmoi add', after computing the parent of each target in the source state tree, chezmoi requires that the parent directory of the target already exists as a SourceStateEntry (i.e. is tracked or implicitly present in the source state). If the walk ends without finding the parent directory entry, it cannot determine where to place the new source file, so it fails.

Source

Thrown at internal/chezmoi/sourcestate.go:496

					}
					continue
				}
				switch sourceStateDir, ok := node.SourceStateEntry.(*SourceStateDir); {
				case i != len(nodes)-1 && !ok:
					panic(fmt.Errorf("nodes[%d]: unexpected non-terminal source state entry, got %T", i, node.SourceStateEntry))
				case ok && sourceStateDir.attr.External:
					targetRelPathComponents := targetRelPath.SplitAll()
					externalDirRelPath := EmptyRelPath.Join(targetRelPathComponents[:i]...)
					externalDirRelPaths.Add(externalDirRelPath)
					if options.Errorf != nil {
						options.Errorf("%s: skipping entries in external_ directory\n", externalDirRelPath)
					}
					continue DEST_ABS_PATH
				}
			}
			parentSourceRelPath = nodes[len(nodes)-1].SourceStateEntry.SourceRelPath()
		} else {
			return fmt.Errorf("%s: parent directory not in source state", destAbsPath)
		}
		nonEmptyDirs.Add(parentSourceRelPath)

		destAbsPathInfo := destAbsPathInfos[destAbsPath]
		var actualStateEntry ActualStateEntry
		var newSourceStateEntry SourceStateEntry
		if destAbsPathInfo == nil {
			// If the destination does not exist then create a new, empty file.
			_, relPath := destAbsPath.Split()
			actualStateEntry = &ActualStateAbsent{
				absPath: destAbsPath,
			}
			fileAttr := FileAttr{
				TargetName: relPath.String(),
				Type:       SourceFileTypeFile,
				Encrypted:  options.Encrypt,
				Empty:      true,
				Template:   options.Template,

View on GitHub (pinned to f901167e46)

Solutions

  1. Check .chezmoiignore (in the source directory) for patterns excluding the target's parent directory and remove/adjust them.
  2. Add the parent directory first (touch a file inside it via 'chezmoi add' of a sibling tracked file) so a directory entry exists.
  3. Verify the source directory structure with 'chezmoi managed' and re-add from a clean state if the source tree was manually edited.

Example fix

// before: dot_config/chezmoi in .chezmoiignore, then
$ chezmoi add ~/.config/chezmoi/extra.conf
// after: remove 'dot_config/chezmoi' from .chezmoiignore, then re-run chezmoi add
Defensive patterns

Strategy: validation

Validate before calling

parent=$(dirname "${1#$HOME/}")
if chezmoi managed | grep -q "^$parent$"; then
  chezmoi add "$1"
else
  echo "parent $parent not managed/ignored? check .chezmoiignore" >&2
fi

Try / catch

// Go caller
if err := sourceState.Update(...); err != nil {
  if strings.Contains(err.Error(), "parent directory not in source state") {
    // add parent dir entry or fix .chezmoiignore, then retry
  }
}

Prevention

When it happens

Trigger: Adding a file whose parent directory cannot be resolved in the source state tree, e.g. adding a deeply nested path when an intermediate directory entry is missing from the source state build (nodes list exhausted without a SourceStateEntry carrying SourceRelPath).

Common situations: Adding a path whose parent is ignored via .chezmoiignore; adding files into directories that exist on disk but are excluded from the source state; inconsistent source state after manual edits to the source directory.

Related errors


AI-assisted analysis of twpayne/chezmoi@f901167e46 (2026-09-01). Data as JSON: /api/errors/b6fd1f040a77c0f9. Report an issue: GitHub.