twpayne/chezmoi · error

%s: %s: path is not relative

Error message

%s: %s: path is not relative

What it means

External target paths must be relative to the target (home) directory. After cleaning, if the path starts with '/' or is absolute (filepath.IsAbs, relevant on Windows e.g. C:\...), chezmoi rejects it because externals may only write inside the target directory.

Source

Thrown at internal/chezmoi/sourcestate.go:1459

		return fmt.Errorf("%s: %w", sourceAbsPath, err)
	}
	externals := make(map[string]External)
	if err := format.Unmarshal(data, &externals); err != nil {
		return fmt.Errorf("%s: %w", sourceAbsPath, err)
	}
	s.mutex.Lock()
	defer s.mutex.Unlock()
	for targetPath, external := range externals {
		if external.TargetPath != "" {
			targetPath = external.TargetPath
		}
		if targetPath == "" {
			return fmt.Errorf("%s: empty path", sourceAbsPath)
		}

		externalPath := path.Clean(targetPath)
		if strings.HasPrefix(externalPath, "/") || filepath.IsAbs(externalPath) {
			return fmt.Errorf("%s: %s: path is not relative", sourceAbsPath, targetPath)
		}
		switch relPath, err := filepath.Rel(".", externalPath); {
		case err != nil:
			return fmt.Errorf("%s: %s: %w", sourceAbsPath, targetPath, err)
		case relPath == ".":
			return fmt.Errorf("%s: %s: empty relative path", sourceAbsPath, targetPath)
		case relPath == "..", strings.HasPrefix(relPath, "../"),
			runtime.GOOS == "windows" && strings.HasPrefix(relPath, ".."+string(filepath.Separator)):
			return fmt.Errorf("%s: %s: relative path outside target directory", sourceAbsPath, targetPath)
		}
		targetRelPath := parentTargetSourceRelPath.JoinString(externalPath)
		external.sourceAbsPath = sourceAbsPath
		s.externals[targetRelPath] = append(s.externals[targetRelPath], &external)
	}
	return nil
}

// addExternalDir adds all externals in externalsDirAbsPath to s.

View on GitHub (pinned to f901167e46)

Solutions

  1. Change the external target path to a path relative to your home directory (e.g. '.local/bin/tool', 'dot_config/tool').
  2. If the external must land outside home, unpack via a script external (type = 'script') that installs to the absolute location itself.
  3. Fix the template variable that renders the absolute path.

Example fix

// before
["/opt/tool"]
type = "archive"
url = "https://example.com/tool.tgz"
// after
[".local/opt/tool"]
type = "archive"
url = "https://example.com/tool.tgz"
Defensive patterns

Strategy: validation

Validate before calling

target := keyOrTargetPath
if strings.HasPrefix(target, "/") || filepath.IsAbs(target) {
  return fmt.Errorf("external path %q must be relative to home", target)
}

Prevention

When it happens

Trigger: An external entry key or TargetPath like '/opt/tool' or 'C:/tools/tool' in .chezmoiexternal, or a template rendering to an absolute path.

Common situations: Users copying install paths from documentation (which often use absolute paths) into external configs; cross-platform externals using Windows absolute paths.

Related errors


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