helm/helm · error

cannot load a directory

Error message

cannot load a directory

What it means

Error from LoadFile in pkg/chart/v2/loader/archive.go. LoadFile is strictly the file (archive) loader: it stats the path and refuses directories outright before attempting to open/read an archive. The generic entry points Loader()/Load() exist precisely to pick DirLoader vs FileLoader based on os.Stat, so seeing this error means the directory-specialized path was bypassed.

Source

Thrown at pkg/chart/v2/loader/archive.go:43

	"helm.sh/helm/v4/pkg/chart/loader/archive"
	chart "helm.sh/helm/v4/pkg/chart/v2"
)

// FileLoader loads a chart from a file
type FileLoader string

// Load loads a chart
func (l FileLoader) Load() (*chart.Chart, error) {
	return LoadFile(string(l))
}

// LoadFile loads from an archive file.
func LoadFile(name string) (*chart.Chart, error) {
	if fi, err := os.Stat(name); err != nil {
		return nil, err
	} else if fi.IsDir() {
		return nil, errors.New("cannot load a directory")
	}

	raw, err := os.Open(name)
	if err != nil {
		return nil, err
	}
	defer raw.Close()

	err = archive.EnsureArchive(name, raw)
	if err != nil {
		return nil, err
	}

	c, err := LoadArchive(raw)
	if err != nil {
		if errors.Is(err, gzip.ErrHeader) {
			return nil, fmt.Errorf("file '%s' does not appear to be a valid chart file (details: %w)", name, err)
		}

View on GitHub (pinned to 2a29f1770b)

Solutions

  1. Use the dispatching API: c, err := c2loader.Loader(path) then l.Load() — it selects DirLoader for directories and FileLoader for files
  2. Or branch explicitly: if fi.IsDir() { c2loader.LoadDir(path) } else { c2loader.LoadFile(path) }
  3. Validate the user-supplied path is a regular file before calling LoadFile, and surface a clear message otherwise

Example fix

// before
func loadChart(p string) (*chart.Chart, error) {
    return c2loader.LoadFile(p)
}

// after
func loadChart(p string) (*chart.Chart, error) {
    l, err := c2loader.Loader(p)
    if err != nil {
        return nil, err
    }
    return l.Load()
}
Defensive patterns

Strategy: validation

Validate before calling

// Validate the path type before choosing the loader
func isRegularFile(p string) (bool, error) {
    fi, err := os.Stat(p)
    if err != nil {
        return false, err
    }
    return !fi.IsDir(), nil
}

Try / catch

l, err := c2loader.Loader(path) // dispatches DirLoader vs FileLoader via os.Stat
if err != nil {
    return nil, err
}
c, err := l.Load()
if err != nil {
    if strings.Contains(err.Error(), "cannot load a directory") {
        return nil, fmt.Errorf("%s is a directory — use LoadDir", path)
    }
    return nil, err
}

Prevention

When it happens

Trigger: Calling c2loader.LoadFile(dirPath) — e.g. SDK code that always assumes a .tgz, glob results that matched a directory, or user-supplied --chart paths that point at an unpacked chart directory.

Common situations: CLI flags accepting either a tgz or a directory and the code hardcoding LoadFile; a download step that should have produced a file but left an extracted directory; testing against local unpacked charts.

Related errors


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