hashicorp/nomad · error

unable to move to %q - alloc dir is not built

Error message

unable to move to %q - alloc dir is not built

What it means

AllocDir.Move relocates a previous allocation's shared data and task-local directories into a new allocation directory. The struct enforces the invariant that Build() was called first: moving into an unbuilt alloc dir returns this error immediately. Previously this was an implicit assumption; now it is explicit validation.

Source

Thrown at client/allocdir/alloc_dir.go:282

				// the snapshotting side closed the connect
				// prematurely and won't try to use the tar
				// anyway.
				a.logger.Warn("snapshotting failed and unable to write error marker", "error", writeErr)
			}
			return fmt.Errorf("failed to snapshot %s: %w", path, err)
		}
	}

	return nil
}

// 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)

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Ensure AllocDir.Build is called (and succeeds) before Move
  2. Check logs for an earlier Build failure and fix its root cause (disk full, permissions)
  3. Re-run the allocation restore so the client rebuilds the dir
  4. In tests, call Build before Move

Example fix

// before
allocDir.Move(oldAllocDir, tasks) // allocDir never built
// after
if err := allocDir.Build(); err != nil { return err }
return allocDir.Move(oldAllocDir, tasks)
Defensive patterns

Strategy: validation

Validate before calling

if !a.built { return errors.New("call Build() before Move()") } // replicate the invariant in caller code

Type guard

func built(d *allocdir.AllocDir) bool { return d != nil } // ensure Build succeeded before Move; track build error explicitly

Try / catch

if err := allocDir.Move(old, tasks); err != nil && strings.Contains(err.Error(), "alloc dir is not built") {
  return fmt.Errorf("restore failed: build alloc dir first: %w", err)
}

Prevention

When it happens

Trigger: Calling Move (as the client runner does during allocation restoration/migration) on an AllocDir whose Build was skipped, failed earlier, or whose built flag was reset — e.g. after a Build error was swallowed upstream.

Common situations: Allocation restore paths where directory build failed silently; tests or custom code constructing AllocDir manually without Build; upgrades where build invariants changed.

Related errors


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