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
- Read the wrapped inner error for the exact invalid mount field
- Set a valid `destination` path inside the task for the mount
- 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
- Always set destination on volume_mount
- Use documented propagation_mode values only
- Match mount options to the volume type (host vs CSI)
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
- Volume Mount (%d) references undefined volume %s
- volume clone ID cannot be updated
- volume snapshot ID cannot be updated
- volume requested capabilities update was not compatible with
- volume topology request update was not compatible with exist
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/83ef9bfdb9b68e4e.
Report an issue: GitHub.