helm/helm · error

file %s already exists and is not a directory

Error message

file %s already exists and is not a directory

What it means

SaveDir() (internal/chart/v3/util/save.go) writes a chart into dest/<chart-name>/ and first stats that target: if a filesystem entry exists at that path but is a regular file rather than a directory, it aborts with this error. This prevents MkdirAll from either failing confusingly or, worse, writing chart files through an existing file path. The chart name itself was already validated by validateName before this point.

Source

Thrown at internal/chart/v3/util/save.go:50

	chart "helm.sh/helm/v4/internal/chart/v3"
	"helm.sh/helm/v4/pkg/chart/common"
)

var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=")

// SaveDir saves a chart as files in a directory.
//
// This takes the chart name, and creates a new subdirectory inside of the given dest
// directory, writing the chart's contents to that subdirectory.
func SaveDir(c *chart.Chart, dest string) error {
	// Create the chart directory
	err := validateName(c.Name())
	if err != nil {
		return err
	}
	outdir := filepath.Join(dest, c.Name())
	if fi, err := os.Stat(outdir); err == nil && !fi.IsDir() {
		return fmt.Errorf("file %s already exists and is not a directory", outdir)
	}
	if err := os.MkdirAll(outdir, 0o755); err != nil {
		return err
	}

	// Save the chart file.
	if err := SaveChartfile(filepath.Join(outdir, ChartfileName), c.Metadata); err != nil {
		return err
	}

	// Save values.yaml
	for _, f := range c.Raw {
		if f.Name == ValuesfileName {
			vf := filepath.Join(outdir, ValuesfileName)
			if err := writeFile(vf, f.Data); err != nil {
				return err
			}
		}

View on GitHub (pinned to 2a29f1770b)

Solutions

  1. Remove or rename the offending file: `rm <dest>/<chart-name>` (or move it elsewhere), then retry SaveDir
  2. Point SaveDir at a fresh destination directory (a new temp dir is the safest)
  3. If the collision comes from case-insensitive filesystems, align the chart name's casing with the existing directory or remove the stale entry

Example fix

# before: dest/mychart is a regular file, SaveDir fails
rm dest/mychart
# after: retry now that the path is free (or use a clean dir)
util.SaveDir(c, "dest")
Defensive patterns

Strategy: validation

Validate before calling

// ensure SaveDir's target is free or a directory before calling it
func ensureSaveDirFree(dest, chartName string) error {
    outdir := filepath.Join(dest, chartName)
    if fi, err := os.Stat(outdir); err == nil && !fi.IsDir() {
        return fmt.Errorf("%s exists and is a file; remove it first", outdir)
    }
    return nil
}

Type guard

func pathIsDirOrMissing(p string) bool {
    fi, err := os.Stat(p)
    return err != nil || fi.IsDir()
}

Try / catch

if err := util.SaveDir(c, dest); err != nil {
    if strings.Contains(err.Error(), "already exists and is not a directory") {
        os.Remove(filepath.Join(dest, c.Name())) // only if safe in your context
        return util.SaveDir(c, dest)
    }
    return err
}

Prevention

When it happens

Trigger: Calling util.SaveDir(c, dest) when dest/<c.Name()> exists as a file — e.g. a leftover 'mychart' file from a previous run, a tarball saved without its extension, or a case-insensitive filesystem collision ('MyChart' vs 'mychart').

Common situations: Re-running `helm chart save` style operations or SDK SaveDir calls after an earlier partial run left a file behind; pulling charts into a directory where a same-named .yaml/.txt file exists; macOS/Windows case-insensitivity turning a previously distinct name into a collision.

Related errors


AI-assisted analysis of helm/helm@2a29f1770b (2026-08-15). Data as JSON: /api/errors/ff09c4786ceedc1a. Report an issue: GitHub.