hashicorp/nomad · error

error opening fifo parent directory %q: %w

Error message

error opening fifo parent directory %q: %w

What it means

fifo.OpenReader opens the parent directory of the FIFO with os.OpenRoot before opening the FIFO itself, using openat2 with O_NOFOLLOW protections. This error wraps a failure opening the FIFO's parent directory, so the reader function cannot even attempt to open the FIFO.

Source

Thrown at client/lib/fifo/fifo_unix.go:38

// so it's advised to run it in a goroutine different from reader goroutine
func CreateAndRead(path string) (func() (io.ReadCloser, error), error) {
	// create first
	if err := mkfifo(path, 0600); err != nil {
		return nil, fmt.Errorf("error creating fifo %v: %w", path, err)
	}

	return func() (io.ReadCloser, error) {
		return OpenReader(path)
	}, nil
}

func OpenReader(path string) (io.ReadCloser, error) {
	dir := filepath.Dir(path)
	base := filepath.Base(path)

	root, err := os.OpenRoot(dir)
	if err != nil {
		return nil, fmt.Errorf("error opening fifo parent directory %q: %w", dir, err)
	}
	defer root.Close()

	// also uses O_NOFOLLOW under the hood
	f, err := root.OpenFile(base, os.O_RDONLY, 0)
	if err != nil {
		return nil, fmt.Errorf("error opening reader at %s: %w", path, err)
	}
	return f, nil
}

// OpenWriter opens a fifo file for writer, assuming it already exists, returns io.WriteCloser
func OpenWriter(path string) (io.WriteCloser, error) {
	dir := filepath.Dir(path)
	base := filepath.Base(path)

	root, err := os.OpenRoot(dir)
	if err != nil {

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Re-run the open after confirming the alloc directory exists — if the allocation was GC'd, re-create the logs directory or restart the task.
  2. Fix ownership/permissions on the alloc logs directory so the Nomad agent user can read it (e.g. chown -R nomad:nomad).
  3. Verify the path component is a real directory, not a file or dangling symlink.
  4. Check client data_dir for corruption; run nomad fs/alloc inspection or restart the client to rebuild alloc dirs.

Example fix

// before: dir removed under the reader
// EACCES on /var/lib/nomad/alloc/.../alloc/logs
// after
$ sudo chown -R nomad:nomad /var/lib/nomad/alloc
$ nomad alloc logs <alloc_id>
Defensive patterns

Strategy: try-catch

Validate before calling

func fifoDirReadable(path string) error {
  dir := filepath.Dir(path)
  st, err := os.Stat(dir)
  if err != nil { return err }
  if !st.IsDir() { return fmt.Errorf("%s is not a directory", dir) }
  return syscall.Access(dir, syscall.R_OK)
}

Type guard

func fifoDirReady(path string) bool {
  st, err := os.Stat(filepath.Dir(path))
  return err == nil && st.IsDir() && syscall.Access(filepath.Dir(path), syscall.R_OK) == nil
}

Try / catch

r, err := fifo.OpenReader(path)
if err != nil {
  var perr *fs.PathError
  if errors.As(err, &perr) && errors.Is(perr.Err, syscall.ENOENT) {
    // alloc was GC'd mid-read: recreate dir and retry once
    os.MkdirAll(filepath.Dir(path), 0755)
    r, err = fifo.OpenReader(path)
  }
  if err != nil { return nil, err }
}

Prevention

When it happens

Trigger: os.OpenRoot(dir) fails inside OpenReader because the directory does not exist (ENOENT — alloc dir cleaned up while reading), permissions deny access (EACCES), or path is not a directory (ENOTDIR, e.g. a file replaced the directory).

Common situations: Following Nomad logs while the allocation is garbage-collected concurrently; permissions on the alloc/logs directory changed (chmod/chown by operator); symlink or file substituted for the logs directory; NFS/overlay quirks returning unexpected errors.

Related errors


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