golang/go · error

ErrInsecurePath

ErrInsecurePath

Error message

archive/tar: insecure file path

What it means

ErrInsecurePath is returned by Reader.Next when a header's Name is non-local per filepath.IsLocal (absolute paths, parent-directory traversals like ../, drive letters on Windows, empty names) AND the GODEBUG knob tarinsecurepath is set to 0. It is a security guard against path traversal during extraction. The header is still returned alongside the error so callers who accept the risk can proceed.

Source

Thrown at src/archive/tar/common.go:38

	"path"
	"reflect"
	"strconv"
	"strings"
	"time"
)

// BUG: Use of the Uid and Gid fields in Header could overflow on 32-bit
// architectures. If a large value is encountered when decoding, the result
// stored in Header will be the truncated version.

var tarinsecurepath = godebug.New("tarinsecurepath")

var (
	ErrHeader          = errors.New("archive/tar: invalid tar header")
	ErrWriteTooLong    = errors.New("archive/tar: write too long")
	ErrFieldTooLong    = errors.New("archive/tar: header field too long")
	ErrWriteAfterClose = errors.New("archive/tar: write after close")
	ErrInsecurePath    = errors.New("archive/tar: insecure file path")
	errMissData        = errors.New("archive/tar: sparse file references non-existent data")
	errUnrefData       = errors.New("archive/tar: sparse file contains unreferenced data")
	errWriteHole       = errors.New("archive/tar: write non-NUL byte in sparse hole")
	errSparseTooLong   = errors.New("archive/tar: sparse map too long")
)

type headerError []string

func (he headerError) Error() string {
	const prefix = "archive/tar: cannot encode header"
	var ss []string
	for _, s := range he {
		if s != "" {
			ss = append(ss, s)
		}
	}
	if len(ss) == 0 {
		return prefix

View on GitHub (pinned to b6b368adc5)

Solutions

  1. If you trust the source, explicitly allow the header: hdr, err := tr.Next(); if err == tar.ErrInsecurePath { err = nil; /* use hdr anyway */ }.
  2. Sanitize names at extraction: strip leading slashes, reject '..', and join with a base dir: clean := filepath.Join(dest, filepath.Clean("/"+hdr.Name)); if !strings.HasPrefix(clean, dest) { skip }.
  3. Set GODEBUG=tarinsecurepath=1 to disable the guard (not recommended for untrusted input).
  4. Prefer the strict mode and skip offending entries rather than disabling the guard globally.

Example fix

// before
for {
  hdr, err := tr.Next()
  if err != nil { return err } // ErrInsecurePath aborts extraction
  os.MkdirAll(filepath.Join(dest, hdr.Name), 0755)
}

// after
for {
  hdr, err := tr.Next()
  if err == io.EOF { break }
  if err == tar.ErrInsecurePath { continue } // skip unsafe names
  if err != nil { return err }
  clean := filepath.Clean(filepath.Join("/", hdr.Name))
  os.MkdirAll(filepath.Join(dest, clean), 0755)
}
Defensive patterns

Strategy: try-catch

Validate before calling

func safeJoin(dest, name string) (string, bool) {
  clean := filepath.Clean(filepath.Join("/", name))
  full := filepath.Join(dest, clean)
  return full, strings.HasPrefix(full+string(os.PathSeparator), dest+string(os.PathSeparator))
}

Type guard

func isLocalName(name string) bool { return filepath.IsLocal(name) }

Try / catch

hdr, err := tr.Next()
if errors.Is(err, tar.ErrInsecurePath) {
  log.Printf("skipping non-local name %q", hdr.Name)
  continue
}

Prevention

When it happens

Trigger: Extracting a tar whose entry Name is absolute (e.g. /etc/passwd), contains .., has a Windows drive letter (C:\x), or is otherwise rejected by filepath.IsLocal, while GODEBUG=tarinsecurepath=0 is active (the forward-compatible / strict mode).

Common situations: Extracting untrusted archives (npm packages, user uploads, scraped downloads); a tarball intentionally containing absolute paths; CI hardened with tarinsecurepath=0; transitioning code before the behavior becomes the Go default.

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/5c8d6bf3b2caaa39. Report an issue: GitHub.