hashicorp/nomad · error

Couldn't read directory %v: %w

Error message

Couldn't read directory %v: %w

What it means

After creating the destination directory for a chroot directory entry, embedDirs enumerates the source host directory with os.ReadDir(source). This error wraps ReadDir's failure for the host source path. It means Nomad could not list the host directory it intends to copy into the chroot.

Source

Thrown at client/allocdir/task_dir.go:265

			uid, gid := getOwner(s)
			if err := linkOrCopy(source, taskEntry, uid, gid, s.Mode().Perm()); err != nil {
				return err
			}

			continue
		}

		// Create destination directory.
		destDir := filepath.Join(t.Dir, dest)

		if err := createDir(t.Dir, dest); err != nil {
			return fmt.Errorf("Couldn't create destination directory %v: %w", destDir, err)
		}

		// Enumerate the files in source.
		dirEntries, err := os.ReadDir(source)
		if err != nil {
			return fmt.Errorf("Couldn't read directory %v: %w", source, err)
		}

		for _, fileEntry := range dirEntries {
			entry, err := fileEntry.Info()
			if err != nil {
				return fmt.Errorf("Couldn't read the file information %v: %w", entry, err)
			}
			hostEntry := filepath.Join(source, entry.Name())
			taskEntry := filepath.Join(destDir, filepath.Base(hostEntry))
			if entry.IsDir() {
				subdirs[hostEntry] = filepath.Join(dest, filepath.Base(hostEntry))
				continue
			}

			// Check if entry exists. This can happen if restarting a failed
			// task.
			if _, err := os.Lstat(taskEntry); err == nil {
				continue

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Check the wrapped errno: EACCES → `chmod/chown` the source dir readable by the nomad user; ENOENT → restore the missing host directory (e.g. reinstall the package providing it)
  2. Identify what deletes the source dir (temp cleaners, config management) and exclude it
  3. Rerun the alloc after restoring the source; chroot entries missing at stat time are skipped silently, so this usually means it existed and vanished
  4. Verify no mount under the source path dropped (dmesg, `mount` output)

Example fix

# before
Couldn't read directory /etc/consul: open /etc/consul: no such file or directory
# after
$ sudo mkdir -p /etc/consul && sudo chmod 755 /etc/consul
$ nomad alloc restart <alloc>
Defensive patterns

Strategy: validation

Validate before calling

// verify every chroot source dir is readable just before Build
for source := range chrootMap {
    if fi, err := os.Stat(source); err == nil && fi.IsDir() {
        if _, err := os.ReadDir(source); err != nil {
            return fmt.Errorf("chroot source %q unreadable: %w", source, err)
        }
    }
}

Try / catch

if err := taskDir.Build(fsi, chroot, username); err != nil {
    if strings.Contains(err.Error(), "Couldn't read directory") {
        // restore/re-permission the host source dir, then retry the alloc
        return retryBuildAfterFix()
    }
    return err
}

Prevention

When it happens

Trigger: os.ReadDir(source) fails while embedding a directory chroot entry — source existed at os.Stat time but was removed/renamed before listing, or is unreadable (EACCES) to the nomad user.

Common situations: TOCTOU race: another process (config management, tmp cleaner) removed the source dir between stat and readdir; permissions tightened on host dirs like /etc or /usr; source is on a mount that went away (network filesystem).

Related errors


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