hashicorp/nomad · error

error opening reader at

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.

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.

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