hashicorp/nomad · error

Volume Mount (%d) is invalid: "%w"

Error message

Volume Mount (%d) is invalid: "%w"

What it means

After existence-checking, Task.Validate runs VolumeMount.Validate for each mount and wraps failures as "Volume Mount (%d) is invalid: \"%w\"". This catches invalid mount properties even when the volume itself is defined, e.g. missing destination, bad propagation mode, or SELinux/readonly constraints.

Source

Thrown at nomad/structs/structs.go:8402

		if t.Leader {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Connect proxy task must not have leader set"))
		}

		// Ensure the proxy task has a corresponding service entry
		serviceErr := ValidateConnectProxyService(t.Kind.Value(), tg.Services)
		if serviceErr != nil {
			mErr.Errors = append(mErr.Errors, serviceErr)
		}
	}

	// Validation for volumes
	for idx, vm := range t.VolumeMounts {
		if _, ok := tg.Volumes[vm.Volume]; !ok {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Volume Mount (%d) references undefined volume %s", idx, vm.Volume))
		}

		if err := vm.Validate(); err != nil {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("Volume Mount (%d) is invalid: \"%w\"", idx, err))
		}
	}

	// Validate CSI Plugin Config
	if t.CSIPluginConfig != nil {
		if t.CSIPluginConfig.ID == "" {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("CSIPluginConfig must have a non-empty PluginID"))
		}

		if !CSIPluginTypeIsValid(t.CSIPluginConfig.Type) {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("CSIPluginConfig PluginType must be one of 'node', 'controller', or 'monolith', got: \"%s\"", t.CSIPluginConfig.Type))
		}

		if t.CSIPluginConfig.StagePublishBaseDir != "" && t.CSIPluginConfig.MountDir != "" &&
			helper.IsSubdirectory(t.CSIPluginConfig.MountDir, t.CSIPluginConfig.StagePublishBaseDir) {
			mErr.Errors = append(mErr.Errors, fmt.Errorf("CSIPluginConfig StagePublishBaseDir must not be a subdirectory of MountDir, got: StagePublishBaseDir=\"%s\" MountDir=\"%s\"", t.CSIPluginConfig.StagePublishBaseDir, t.CSIPluginConfig.MountDir))
		}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Read the wrapped inner error for the exact invalid mount field
  2. Set a valid `destination` path inside the task for the mount
  3. Use propagation_mode only where supported (host volumes) and with valid values (private/host-to-task)

Example fix

// before
volume_mount {
  volume = "data"
}
// after
volume_mount {
  volume      = "data"
  destination = "/data"
}
Defensive patterns

Strategy: validation

Validate before calling

for i, vm := range task.VolumeMounts {
    if err := vm.Validate(); err != nil {
        return fmt.Errorf("volume_mount %d: %w", i, err)
    }
}

Try / catch

if err := job.Validate(); err != nil {
    if strings.Contains(err.Error(), "is invalid:") && strings.Contains(err.Error(), "Volume Mount") { /* fix mount fields */ }
}

Prevention

When it happens

Trigger: A task volume_mount has an empty `destination`, an invalid `propagation_mode`, or other field failing VolumeMount.Validate, while the referenced group volume exists.

Common situations: Omitting destination inside the container; setting propagation_mode to an unsupported string; mount options incompatible with the volume type (host vs CSI).

Related errors


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