hashicorp/nomad · error

error moving data dir: %w

Error message

error moving data dir: %w

What it means

Move renames the previous allocation's SharedDataDir into the new allocation dir with os.Rename. If the rename fails (cross-device filesystems, permissions, source vanished), the error is wrapped as 'error moving data dir'. Rename requires both paths on the same mounted filesystem.

Source

Thrown at client/allocdir/alloc_dir.go:294

// Move other alloc directory's shared path and local dir to this alloc dir.
func (a *AllocDir) Move(other Interface, tasks []*structs.Task) error {
	a.mu.RLock()
	if !a.built {
		// Enforce the invariant that Build is called before Move
		a.mu.RUnlock()
		return fmt.Errorf("unable to move to %q - alloc dir is not built", a.AllocDir)
	}

	// Moving is slow and only reads immutable fields, so unlock during heavy IO
	a.mu.RUnlock()

	// Move the data directory
	otherDataDir := filepath.Join(other.ShareDirPath(), SharedDataDir)
	dataDir := filepath.Join(a.SharedDir, SharedDataDir)
	if fileInfo, err := os.Stat(otherDataDir); fileInfo != nil && err == nil {
		os.Remove(dataDir) // remove an empty data dir if it exists
		if err := os.Rename(otherDataDir, dataDir); err != nil {
			return fmt.Errorf("error moving data dir: %w", err)
		}
	}

	// Move the task directories
	for _, task := range tasks {
		otherTaskDir := filepath.Join(other.AllocDirPath(), task.Name)
		otherTaskLocal := filepath.Join(otherTaskDir, TaskLocal)

		fileInfo, err := os.Stat(otherTaskLocal)
		if fileInfo != nil && err == nil {
			// TaskDirs haven't been built yet, so create it
			newTaskDir := filepath.Join(a.AllocDir, task.Name)
			if err := os.MkdirAll(newTaskDir, fileMode777); err != nil {
				return fmt.Errorf("error creating task %q dir: %w", task.Name, err)
			}
			localDir := filepath.Join(newTaskDir, TaskLocal)
			os.Remove(localDir) // remove an empty local dir if it exists
			if err := os.Rename(otherTaskLocal, localDir); err != nil {

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Ensure old and new alloc dirs are on the same filesystem/device
  2. Check directory permissions on both alloc dirs
  3. Verify the source data dir still exists (not GC'd) at migration time
  4. Copy-then-delete as a fallback when cross-device moves are unavoidable

Example fix

// before
if err := os.Rename(otherDataDir, dataDir); err != nil {
  return fmt.Errorf("error moving data dir: %w", err)
}
// after
if err := os.Rename(otherDataDir, dataDir); err != nil {
  if err2 := copyDir(otherDataDir, dataDir); err2 != nil {
    return fmt.Errorf("error moving data dir: %w", err)
  }
  os.RemoveAll(otherDataDir)
}
Defensive patterns

Strategy: try-catch

Validate before calling

od, err := os.Stat(otherDataDir)
if err == nil && od != nil {
  sameDev := syscall.Stat_t{ }
  _ = syscall.Stat(otherDataDir, &sameDev) // compare Device with target's before rename
}

Try / catch

if err := allocDir.Move(other, tasks); err != nil && strings.Contains(err.Error(), "error moving data dir") {
  // fall back to copy+delete or surface EXDEV/mount misconfiguration
}

Prevention

When it happens

Trigger: os.Rename of otherDataDir fails — source alloc dir on a different device/mount than the target, permission denial, or the source data dir removed concurrently by GC.

Common situations: Alloc dirs spanned across volumes/bind-mounts (host volume layouts); node filesystem remounts read-only; GC racing with migration; permission mismatches after node re-provisioning.

Related errors


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