{"record":{"id":"ff09c4786ceedc1a","repo":"helm/helm","slug":"file-s-already-exists-and-is-not-a-directory-ff09c4","errorCode":null,"errorMessage":"file %s already exists and is not a directory","messagePattern":"file (.+?) already exists and is not a directory","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/chart/v3/util/save.go","lineNumber":50,"sourceCode":"\tchart \"helm.sh/helm/v4/internal/chart/v3\"\n\t\"helm.sh/helm/v4/pkg/chart/common\"\n)\n\nvar headerBytes = []byte(\"+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=\")\n\n// SaveDir saves a chart as files in a directory.\n//\n// This takes the chart name, and creates a new subdirectory inside of the given dest\n// directory, writing the chart's contents to that subdirectory.\nfunc SaveDir(c *chart.Chart, dest string) error {\n\t// Create the chart directory\n\terr := validateName(c.Name())\n\tif err != nil {\n\t\treturn err\n\t}\n\toutdir := filepath.Join(dest, c.Name())\n\tif fi, err := os.Stat(outdir); err == nil && !fi.IsDir() {\n\t\treturn fmt.Errorf(\"file %s already exists and is not a directory\", outdir)\n\t}\n\tif err := os.MkdirAll(outdir, 0o755); err != nil {\n\t\treturn err\n\t}\n\n\t// Save the chart file.\n\tif err := SaveChartfile(filepath.Join(outdir, ChartfileName), c.Metadata); err != nil {\n\t\treturn err\n\t}\n\n\t// Save values.yaml\n\tfor _, f := range c.Raw {\n\t\tif f.Name == ValuesfileName {\n\t\t\tvf := filepath.Join(outdir, ValuesfileName)\n\t\t\tif err := writeFile(vf, f.Data); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/helm/helm/blob/2a29f1770b62844b27197d2507377361d45ad7c0/internal/chart/v3/util/save.go#L32-L68","documentation":"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.","triggerScenarios":"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').","commonSituations":"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.","solutions":["Remove or rename the offending file: `rm <dest>/<chart-name>` (or move it elsewhere), then retry SaveDir","Point SaveDir at a fresh destination directory (a new temp dir is the safest)","If the collision comes from case-insensitive filesystems, align the chart name's casing with the existing directory or remove the stale entry"],"exampleFix":"# before: dest/mychart is a regular file, SaveDir fails\nrm dest/mychart\n# after: retry now that the path is free (or use a clean dir)\nutil.SaveDir(c, \"dest\")","handlingStrategy":"validation","validationCode":"// ensure SaveDir's target is free or a directory before calling it\nfunc ensureSaveDirFree(dest, chartName string) error {\n    outdir := filepath.Join(dest, chartName)\n    if fi, err := os.Stat(outdir); err == nil && !fi.IsDir() {\n        return fmt.Errorf(\"%s exists and is a file; remove it first\", outdir)\n    }\n    return nil\n}","typeGuard":"func pathIsDirOrMissing(p string) bool {\n    fi, err := os.Stat(p)\n    return err != nil || fi.IsDir()\n}","tryCatchPattern":"if err := util.SaveDir(c, dest); err != nil {\n    if strings.Contains(err.Error(), \"already exists and is not a directory\") {\n        os.Remove(filepath.Join(dest, c.Name())) // only if safe in your context\n        return util.SaveDir(c, dest)\n    }\n    return err\n}","preventionTips":["Save charts into fresh temp dirs per run (os.MkdirTemp) instead of shared destinations","On case-insensitive filesystems (macOS/Windows), avoid chart names differing only by case","Clean stale outputs in CI between runs"],"tags":["filesystem","chart","save","save-dir"],"backgroundTag":null,"analyzedSha":"2a29f1770b62844b27197d2507377361d45ad7c0","analyzedAt":"2026-08-15T22:02:47.490Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}