hashicorp/nomad · error

error evaluating symlink: %w

Error message

error evaluating symlink: %w

What it means

After creating a symlink from the archive, Nomad validates both the entry name and its link target with escapingfs.PathEscapesAllocDir to prevent path traversal. This error is returned when that security check itself fails (internal error evaluating paths), not when the symlink actually escapes.

Source

Thrown at client/allocwatcher/alloc_watcher.go:635

			os.MkdirAll(name, os.FileMode(hdr.Mode))

			// Can't change owner if not root or on Windows.
			if euid == 0 {
				if err := os.Chown(name, hdr.Uid, hdr.Gid); err != nil {
					return fmt.Errorf("error chowning directory %w", err)
				}
			}
			continue
		}
		// If the header is for a symlink we create the symlink
		if hdr.Typeflag == tar.TypeSymlink {
			if err = os.Symlink(hdr.Linkname, filepath.Join(dest, hdr.Name)); err != nil {
				return fmt.Errorf("error creating symlink: %w", err)
			}

			for _, path := range []string{hdr.Name, hdr.Linkname} {
				if escapes, err := escapingfs.PathEscapesAllocDir(dest, "", path); err != nil {
					return fmt.Errorf("error evaluating symlink: %w", err)
				} else if escapes {
					return fmt.Errorf("archive contains symlink that escapes alloc dir")
				}
			}

			continue
		}
		// If the header is a file, we write to a file
		if hdr.Typeflag == tar.TypeReg {
			fPath := filepath.Join(dest, hdr.Name)
			if _, err := os.Lstat(fPath); err == nil {
				if err := os.Remove(fPath); err != nil {
					return fmt.Errorf("error removing existing file: %w", err)
				}
			}
			f, err := os.Create(fPath)
			if err != nil {
				return fmt.Errorf("error creating file: %w", err)

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Inspect the archive for malformed/invalid path entries (invalid characters, absolute paths)
  2. Re-snapshot or re-migrate the alloc dir to regenerate a clean archive
  3. Check escapingfs.PathEscapesAllocDir implementation and host filesystem errors in logs
  4. Reject the migration and redeploy the job instead of retrying with a corrupt archive
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate archive paths are well-formed before extraction:
// reject headers whose Name/Linkname contain NUL bytes, absolute paths, or invalid separators

Type guard

func malformedArchivePath(p string) bool {
    return p == "" || strings.ContainsRune(p, 0) || filepath.IsAbs(p)
}

Try / catch

if err := watcher.Wait(ctx); err != nil {
    if strings.Contains(err.Error(), "error evaluating symlink") {
        log.Error("archive evaluation failed; treat as corrupt", "err", err)
    }
    return err
}

Prevention

When it happens

Trigger: escapingfs.PathEscapesAllocDir(dest, "", path) returns a non-nil error for either hdr.Name or hdr.Linkname during tar extraction of a TypeSymlink entry.

Common situations: Malformed paths in a hand-crafted or corrupted archive (e.g. NUL bytes, invalid separators) that break path evaluation; filesystem errors during evaluation.

Related errors


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