helm/helm · error

error evaluating symlink %s: %w

Error message

error evaluating symlink %s: %w

What it means

internal/sympath implements a symlink-following replacement for filepath.Walk, and Helm uses it when loading a chart from a directory tree. This error fires when the walker hits a symlink and filepath.EvalSymlinks cannot resolve it; the wrapped error is the OS reason (target missing, permission denied on a path component, or a symlink loop). Because chart loading goes through this walker, a single broken symlink anywhere under the chart root aborts the whole load. Note that Helm deliberately follows chart symlinks (it logs an info message about it), so it does not skip bad ones.

Source

Thrown at internal/sympath/walk.go:72

	if err != nil {
		return nil, err
	}
	names, err := f.Readdirnames(-1)
	f.Close()
	if err != nil {
		return nil, err
	}
	sort.Strings(names)
	return names, nil
}

// symwalk recursively descends path, calling walkFn.
func symwalk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
	// Recursively walk symlinked directories.
	if IsSymlink(info) {
		resolved, err := filepath.EvalSymlinks(path)
		if err != nil {
			return fmt.Errorf("error evaluating symlink %s: %w", path, err)
		}
		// This log message is to highlight a symlink that is being used within a chart, symlinks can be used for nefarious reasons.
		slog.Info("found symbolic link in path. Contents of linked file included and used", "path", path, "resolved", resolved)
		if info, err = os.Lstat(resolved); err != nil {
			return err
		}
		if err := symwalk(path, info, walkFn); err != nil && !errors.Is(err, filepath.SkipDir) {
			return err
		}
		return nil
	}

	if err := walkFn(path, info, nil); err != nil {
		return err
	}

	if !info.IsDir() {
		return nil

View on GitHub (pinned to 2a29f1770b)

Solutions

  1. List the dangling links in the chart: find <chart-dir> -xtype l (or ls -la on the path named in the error)
  2. Fix or delete each broken symlink reported in the error message
  3. If the link is intentional, make it relative to the chart root and verify it resolves on the machine running Helm
  4. Re-create the distribution artifact with helm package from the repaired directory

Example fix

# before: templates/config.yaml -> /etc/myapp/config.yaml (target absent on this host)
ln -sfn ../../files/config.yaml mychart/templates/config.yaml

# after
helm package mychart  # succeeds
Defensive patterns

Strategy: validation

Validate before calling

// before loading or packaging a chart directory, scan for unresolvable symlinks
err := filepath.WalkDir(chartDir, func(p string, d fs.DirEntry, err error) error {
	if err != nil {
		return err
	}
	if d.Type()&fs.ModeSymlink != 0 {
		if _, err := filepath.EvalSymlinks(p); err != nil {
			return fmt.Errorf("broken symlink %s: %w", p, err)
		}
	}
	return nil
})

Try / catch

if err := loader.Load(chartDir); err != nil {
	var linkErr *fs.PathError
	if errors.As(err, &linkErr) && strings.Contains(err.Error(), "error evaluating symlink") {
		// point the user at the broken symlink named in the message
	}
}

Prevention

When it happens

Trigger: Loading a chart from a directory (helm install ./mychart, helm package ./mychart, SDK chart load with a local path) where the tree contains a dangling symlink, a symlink into a directory the process cannot traverse, or two symlinks pointing at each other. The tar format preserves symlinks, so an unpacked .tgz can carry a link that was valid on the packager's machine but not on yours.

Common situations: Charts checked into git with symlinks that resolve only on the original author's machine (e.g., into /etc or a home dir); unpacked chart archives containing absolute symlinks; build pipelines copying node_modules-style or shared-config layouts into a chart; CI runners with restricted directory permissions; accidental symlink cycles created while reorganizing templates.

Related errors


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