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.

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.

Appendix: 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)