{"record":{"id":"6ce99e29edfa69e8","repo":"hashicorp/nomad","slug":"no-host-volume-named-s","errorCode":null,"errorMessage":"no host volume named: %s","messagePattern":"no host volume named: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/allocrunner/taskrunner/volume_hook.go","lineNumber":90,"sourceCode":"\t\t\t// type.\n\t\t\tcontinue\n\t\t}\n\n\t\t// This is a defensive check, but this function should only ever receive\n\t\t// host-type volumes.\n\t\tif req.Type != structs.VolumeTypeHost {\n\t\t\tcontinue\n\t\t}\n\n\t\tsource := req.Source\n\t\tif req.PerAlloc {\n\t\t\tsource = source + structs.AllocSuffix(allocName)\n\t\t}\n\t\thostVolume, ok := clientVolumesByName[source]\n\t\tif !ok {\n\t\t\t// Should never happen, but unless the client volumes were mutated during\n\t\t\t// the execution of this hook.\n\t\t\treturn nil, fmt.Errorf(\"no host volume named: %s\", source)\n\t\t}\n\n\t\tmcfg := &drivers.MountConfig{\n\t\t\tRequestName:     hostVolume.Name,\n\t\t\tHostPath:        hostVolume.Path,\n\t\t\tTaskPath:        m.Destination,\n\t\t\tReadonly:        hostVolume.ReadOnly || req.ReadOnly || m.ReadOnly,\n\t\t\tPropagationMode: m.PropagationMode,\n\t\t\tSELinuxLabel:    m.SELinuxLabel,\n\t\t}\n\t\tmounts = append(mounts, mcfg)\n\t}\n\n\treturn mounts, nil\n}\n\n// partitionVolumesByType takes a map of volume-alias to volume-request and\n// returns them in the form of volume-type:(volume-alias:volume-request)","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/allocrunner/taskrunner/volume_hook.go#L72-L108","documentation":"hostVolumeMountConfigurations builds driver MountConfigs from the task's volume_mounts. It looks each source up in the client's host volumes; if absent it returns \"no host volume named: <source>\". The comment notes this should never happen because validation ran first — it guards against client volumes being mutated between validation and mount generation.","triggerScenarios":"Client node HostVolumes map changed (or was empty) between validateHostVolumes and hostVolumeMountConfigurations during the same Prestart — e.g. the node config was reloaded concurrently or validation was skipped via a different code path.","commonSituations":"Race with agent config reload (SIGHUP) during alloc start; bespoke tooling mutating client config at runtime; identical to the missing-volume case but symptomatic of an in-flight change.","solutions":["Restart the allocation after ensuring the client's host_volume config is stable","Avoid reloading/reconfiguring the Nomad agent while allocations are starting","Verify the volume name matches the client config exactly (case, per_alloc suffix)","If it recurs, collect a nomad agent log bundle — it indicates an internal race"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// before scheduling, confirm client host volumes are stable\nvols := clientConfig.Node.HostVolumes\nfor _, m := range task.VolumeMounts {\n    if _, ok := vols[m.Source]; !ok { skipNode() }\n}","typeGuard":null,"tryCatchPattern":"mcfgs, err := hostVolumeMountConfigurations(mounts, volumes, hostVolumes, name)\nif err != nil {\n    // transient client-volume mutation; re-read client config and retry once\n    hostVolumes = refreshClientVolumes()\n    return hostVolumeMountConfigurations(mounts, volumes, hostVolumes, name)\n}","preventionTips":["Avoid SIGHUP/config reloads of the agent while allocations are starting","Treat this error as a race: reschedule rather than hot-fixing config mid-start","Keep client volume config immutable except during maintenance windows","If it recurs without config changes, capture agent logs for support"],"tags":["nomad","volumes","race-condition","host-volume"],"backgroundTag":"missing-host-volume","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}