helm/helm · error
failed to extract archive: %w
Error message
failed to extract archive: %w
What it means
TarGzExtractor.Extract failed while unpacking the local tarball into the temp dir. Distinct inner causes: gzip.NewReader error (file is not gzip — e.g. a zip renamed to .tgz), tar stream error mid-read (truncation), cleanJoin rejecting an entry name ('..', ':', absolute path), an entry typeflag outside TypeDir/TypeReg (symlinks and hardlinks hit the 'unknown type' branch and are rejected), or target-file creation failures (permissions, ENOSPC).
Source
Thrown at internal/plugin/installer/local_installer.go:146
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)
// Extract the archive
buffer := bytes.NewBuffer(data)
if err := i.extractor.Extract(buffer, tempDir); err != nil {
return fmt.Errorf("failed to extract archive: %w", err)
}
// Plugin directory should be named after the plugin at the archive root
pluginName := stripPluginName(filepath.Base(i.Source))
pluginDir := filepath.Join(tempDir, pluginName)
if _, err = os.Stat(filepath.Join(pluginDir, "plugin.yaml")); err != nil {
return fmt.Errorf("plugin.yaml not found in expected directory %s: %w", pluginDir, err)
}
// Copy to the final destination
slog.Debug("copying", "source", pluginDir, "path", i.Path())
return fs.CopyDir(pluginDir, i.Path())
}
// Update updates a local repository
func (i *LocalInstaller) Update() error {
slog.Debug("local repository is auto-updated")
return nilView on GitHub (pinned to 2a29f1770b)
Solutions
- Inspect entries: tar -tvf myplugin-1.0.0.tgz — look for 'l' (symlink) or 'h' (hardlink) types and for names starting with / or containing ..
- Repackage with regular files only and relative paths: cd build && tar -czf ../myplugin-1.0.0.tgz myplugin/
- Confirm the file is genuinely gzip: file myplugin-1.0.0.tgz
- Ensure TMPDIR is writable and has space
Example fix
# before: macOS tar keeps symlink ln -s ../shared/binary myplugin/bin && tar -czf myplugin-1.0.0.tgz myplugin # after: real file, relative paths cp ../shared/binary myplugin/bin && tar -czf myplugin-1.0.0.tgz myplugin
Defensive patterns
Strategy: validation
Validate before calling
// Reject archives that TarGzExtractor cannot handle before installing: only dirs and regular files, safe relative names.
func extractionSafeTgz(p string) error {
f, _ := os.Open(p)
defer f.Close()
gz, err := gzip.NewReader(f)
if err != nil {
return fmt.Errorf("not gzip: %w", err)
}
tr := tar.NewReader(gz)
for {
h, err := tr.Next()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
if h.Typeflag != tar.TypeDir && h.Typeflag != tar.TypeReg && h.Typeflag != tar.TypeXHeader && h.Typeflag != tar.TypeXGlobalHeader {
return fmt.Errorf("entry %s has unsupported type %b (symlinks/hardlinks not supported)", h.Name, h.Typeflag)
}
if path.IsAbs(h.Name) || strings.Contains(h.Name, ":") || strings.Contains(filepath.ToSlash(h.Name), "..") {
return fmt.Errorf("entry %s has an unsafe name", h.Name)
}
}
} Prevention
- Package plugin tarballs without symlinks or hardlinks — copy real files
- Create tarballs from inside the build root so all entry names are relative
- Run tar -tvf on new plugin packages as a CI gate
When it happens
Trigger: helm plugin install ./myplugin-1.0.0.tgz where the archive was created on macOS with symlinks (tar preserves them as TypeSymlink entries, unsupported here); archive contains ./ or absolute entries; truncated file; read-only TMPDIR.
Common situations: macOS/Linux packaging differences (symlinked binaries inside plugin dirs); files zipped then renamed; incomplete scp/ftp transfers.
Related errors
- extracting files from archive: %w
- failed to extract plugin metadata from tarball: %w
- path contains ':', which is illegal
- path contains '..', which is illegal
- path is absolute, which is illegal
AI-assisted analysis of helm/helm@2a29f1770b (2026-08-15).
Data as JSON: /api/errors/6126772f20cf2df0.
Report an issue: GitHub.