hashicorp/nomad · error

archive contains object that escapes alloc dir

Error message

archive contains object that escapes alloc dir

What it means

If PathEscapesAllocDir determines that a tar entry (hdr.Name) would land outside the destination allocation directory, streamAllocDir refuses it with the literal error 'archive contains object that escapes alloc dir'. This is a security guard against path traversal (e.g. ../../) or absolute-path/symlink attacks carried inside the snapshot received from another node.

Source

Thrown at client/allocwatcher/alloc_watcher.go:599

	buf := make([]byte, 1024)
	for !canceled() {
		// Get the next header
		hdr, err := tr.Next()

		// Snapshot has ended
		if err == io.EOF {
			return nil
		}

		if err != nil {
			return fmt.Errorf("error streaming previous alloc %q for new alloc %q: %w",
				p.prevAllocID, p.allocID, err)
		}

		if escapes, err := escapingfs.PathEscapesAllocDir(dest, "", hdr.Name); err != nil {
			return fmt.Errorf("error evaluating object: %w", err)
		} else if escapes {
			return fmt.Errorf("archive contains object that escapes alloc dir")
		}

		if hdr.Name == errorFilename {
			// Error snapshotting on the remote side, try to read
			// the message out of the file and return it.
			errBuf := make([]byte, int(hdr.Size))
			if _, err := tr.Read(errBuf); err != nil && err != io.EOF {
				return fmt.Errorf("error streaming previous alloc %q for new alloc %q; failed reading error message: %w",
					p.prevAllocID, p.allocID, err)
			}
			return fmt.Errorf("error streaming previous alloc %q for new alloc %q: %s",
				p.prevAllocID, p.allocID, string(errBuf))
		}

		// If the header is for a directory we create the directory
		if hdr.Typeflag == tar.TypeDir {
			name := filepath.Join(dest, hdr.Name)
			os.MkdirAll(name, os.FileMode(hdr.Mode))

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Inspect the previous alloc dir for symlinks or paths escaping the allocation directory and fix/remove them before rescheduling
  2. Verify the source node's integrity — this error can indicate a malicious snapshot; investigate the node
  3. Ensure tasks do not create symlinks outside their alloc dir (chroot/ isolation settings, task guidance)
  4. Upgrade Nomad if running a version with known escapingfs gaps, then retry the migration

Example fix

// before (inside task)
ln -s /etc/passwd $NOMAD_ALLOC_DIR/leak
// after
ln -s $NOMAD_ALLOC_DIR/data/link-target $NOMAD_ALLOC_DIR/link  # keep symlinks inside the alloc dir
Defensive patterns

Strategy: type-guard

Validate before calling

func entryEscapes(dest, name string) bool {
	escapes, _ := escapingfs.PathEscapesAllocDir(dest, "", name)
	return escapes
}

Type guard

func isSafeEntry(dest string, hdr *tar.Header) bool {
	escapes, err := escapingfs.PathEscapesAllocDir(dest, "", hdr.Name)
	return err == nil && !escapes
}

Try / catch

if err := migrate(); err != nil {
	if strings.Contains(err.Error(), "escapes alloc dir") {
		// security event: alert, quarantine snapshot, audit source node
	}
	return err
}

Prevention

When it happens

Trigger: The snapshot tar from the previous allocation contains an entry whose resolved path escapes dest: names with '..' components, absolute paths, or symlinks (including hardlink/linkname entries) that resolve outside the alloc dir.

Common situations: A compromised or malicious node sending a crafted snapshot during migration; tasks that created symlinks pointing outside their alloc dir; a buggy task writing paths with ../; fuzzed/corrupted archive producing path escapes.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/4e955f30d9896264. Report an issue: GitHub.