helm/helm · error

failed to save tarball: %w

Error message

failed to save tarball: %w

What it means

After extracting metadata, LocalInstaller writes the original tarball bytes to <HELM_DATA_HOME>/plugins/<name>-<version>.tgz so future --verify runs have the exact artifact; os.WriteFile on that path failed. Causes: the plugins data directory is not writable, a leftover directory (not file) already exists at that exact filename, or the filesystem is full.

Source

Thrown at internal/plugin/installer/local_installer.go:124

	// Read the archive file
	data, err := os.ReadFile(i.Source)
	if err != nil {
		return fmt.Errorf("failed to read archive: %w", err)
	}

	// Copy the original tarball to plugins directory for verification
	// Extract metadata to get the actual plugin name and version
	metadata, err := plugin.ExtractTgzPluginMetadata(bytes.NewReader(data))
	if err != nil {
		return fmt.Errorf("failed to extract plugin metadata from tarball: %w", err)
	}
	filename := fmt.Sprintf("%s-%s.tgz", metadata.Name, metadata.Version)
	tarballPath := helmpath.DataPath("plugins", filename)
	if err := os.MkdirAll(filepath.Dir(tarballPath), 0o755); err != nil {
		return fmt.Errorf("failed to create plugins directory: %w", err)
	}
	if err := os.WriteFile(tarballPath, data, 0o644); err != nil {
		return fmt.Errorf("failed to save tarball: %w", err)
	}

	// Check for and copy .prov file if it exists
	provSource := i.Source + ".prov"
	if provData, err := os.ReadFile(provSource); err == nil {
		provPath := tarballPath + ".prov"
		if err := os.WriteFile(provPath, provData, 0o644); err != nil {
			slog.Debug("failed to save provenance file", "error", err)
		}
	}

	// Create a temporary directory for extraction
	tempDir, err := os.MkdirTemp("", "helm-plugin-extract-")
	if err != nil {
		return fmt.Errorf("failed to create temp directory: %w", err)
	}
	defer os.RemoveAll(tempDir)

View on GitHub (pinned to 2a29f1770b)

Solutions

  1. Check the target: ls -la $(helm env HELM_DATA_HOME)/plugins/myplugin-1.0.0.tgz — if it is a directory, remove it
  2. Fix ownership of the plugins data dir: chown -R $(id -u):$(id -g) <data>/plugins or avoid mixing sudo and non-sudo Helm
  3. Free disk space if the volume is full (df -h)
  4. Retry the install after cleaning the colliding entry

Example fix

# before: leftover directory blocks the tarball path
ls -d ~/.local/share/helm/plugins/myplugin-1.0.0.tgz/  # it's a dir
# after: remove collision, reinstall
rm -rf ~/.local/share/helm/plugins/myplugin-1.0.0.tgz && helm plugin install ./myplugin-1.0.0.tgz
Defensive patterns

Strategy: validation

Validate before calling

func tarballSlotFree(name, version string) error {
	target := helmpath.DataPath("plugins", fmt.Sprintf("%s-%s.tgz", name, version))
	if fi, err := os.Lstat(target); err == nil && fi.IsDir() {
		return fmt.Errorf("a directory occupies %s — remove it first", target)
	}
	return nil
}

Prevention

When it happens

Trigger: A previous partial install left a directory named myplugin-1.0.0.tgz in the plugins data dir; plugins dir owned by root after running Helm under sudo; ENOSPC; two different plugins colliding on the same name-version filename.

Common situations: Switching between sudo helm and plain helm (root-owned state); CI runners with tiny disks; uninstalling a plugin manually (rm -rf of the dir but not the saved tarball) leaving inconsistent state.

Related errors


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