hashicorp/nomad · error

error opening reader at %s: %w

Error message

error opening reader at %s: %w

What it means

OpenReader opens an existing named pipe (FIFO) for reading. It resolves the FIFO relative to its parent directory via os.Root (O_NOFOLLOW) and fails with this wrapped error when the underlying OpenFile on the FIFO itself fails. The wrapped cause is typically ENOENT, EACCES, or a symlink/path-traversal rejection.

Source

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

	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 {
		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_WRONLY, 0)
	if err != nil {

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Ensure the FIFO exists first (fifo.Create or OpenWriter side) before calling OpenReader, or retry until it appears
  2. Verify the path exists and is a FIFO: os.Stat / unix.Mode(fileMode) & os.ModeNamedPipe
  3. Remove stale FIFOs from prior runs (fifo.Remove) and recreate them
  4. Check filesystem permissions and security-module denials in the wrapped error

Example fix

// before
r, err := fifo.OpenReader("/run/containerd/io/stdout")
// after
if _, err := os.Stat("/run/containerd/io/stdout"); os.IsNotExist(err) {
	if err := fifo.Create("/run/containerd/io/stdout", 0o600); err != nil { return err }
}
r, err := fifo.OpenReader("/run/containerd/io/stdout")
Defensive patterns

Strategy: retry

Validate before calling

func ensureFifo(path string) error {
	fi, err := os.Stat(path)
	if err != nil {
		return err
	}
	if fi.Mode()&os.ModeNamedPipe == 0 {
		return fmt.Errorf("%s is not a fifo", path)
	}
	return nil
}

Type guard

func isNotExistErr(err error) bool { return errors.Is(err, fs.ErrNotExist) }

Try / catch

r, err := fifo.OpenReader(path)
if err != nil {
	var pe *fs.PathError
	if errors.As(err, &pe) && errors.Is(pe.Err, fs.ErrNotExist) {
		// wait/retry until writer creates the fifo
	}
	return fmt.Errorf("open reader %s: %w", path, err)
}

Prevention

When it happens

Trigger: Calling fifo.OpenReader(path) when the FIFO does not exist at that path, when the caller lacks read permission on it, when the path component is a symlink, or when an I/O error occurs opening the file.

Common situations: Log-monitor consumers starting before the writer creates the FIFO; stale FIFO left behind by a crashed previous run; path typos or container path mismatches (mount not present); SELinux/AppArmor denying read access to the pipe file.

Related errors


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