hashicorp/nomad · error
validation error: %v
Error message
validation error: %v
What it means
NodePublishVolume validates its *NodePublishVolumeRequest (via req.Validate()) before sending the CSI RPC, and returns 'validation error: %v' when any required field is missing — the Validate method on that struct checks for empty VolumeID/ExternalID, TargetPath, StagingTargetPath, and similar required request fields. This is a local precondition failure; the gRPC call is never made. Unlike the NodeUnstage checks, it aggregates whatever req.Validate reports.
Source
Thrown at plugins/csi/client.go:864
code := status.Code(err)
switch code {
case codes.NotFound:
err = fmt.Errorf("%w: volume %q could not be found: %v",
structs.ErrCSIClientRPCIgnorable, volumeID, err)
case codes.Internal:
err = fmt.Errorf("node plugin returned an internal error, check the plugin allocation logs for more information: %v", err)
}
}
return err
}
func (c *client) NodePublishVolume(ctx context.Context, req *NodePublishVolumeRequest, opts ...grpc.CallOption) error {
if err := c.ensureConnected(ctx); err != nil {
return err
}
if err := req.Validate(); err != nil {
return fmt.Errorf("validation error: %v", err)
}
// NodePublishVolume's response contains no extra data. If err == nil, we were
// successful.
_, err := c.nodeClient.NodePublishVolume(ctx, req.ToCSIRepresentation(), opts...)
if err != nil {
code := status.Code(err)
switch code {
case codes.NotFound:
err = fmt.Errorf("volume %q could not be found: %v", req.ExternalID, err)
case codes.AlreadyExists:
err = fmt.Errorf(
"volume %q is already published at target path %q but with capabilities or a read_only setting incompatible with this request: %v",
req.ExternalID, req.TargetPath, err)
case codes.FailedPrecondition:
err = fmt.Errorf("volume %q does not have MULTI_NODE volume capability: %v",
req.ExternalID, err)
case codes.Internal:View on GitHub (pinned to 482b49bf1a)
Solutions
- Populate all required fields (ExternalID, TargetPath, StagingTargetPath, and required capability fields) before calling
- Call req.Validate() yourself beforehand to get the precise missing-field message
- Check how the task runner builds the request; trace which allocation volume field is empty
- Diff your code against the current NodePublishVolumeRequest definition for newly added required fields
Example fix
// before
req := &csi.NodePublishVolumeRequest{}
c.Client.NodePublishVolume(ctx, req)
// after
req := &csi.NodePublishVolumeRequest{
ExternalID: vol.ID,
TargetPath: targetPath,
StagingTargetPath: stagingPath,
CopyMode: false,
}
if err := req.Validate(); err != nil {
return fmt.Errorf("publish request invalid: %w", err)
}
c.Client.NodePublishVolume(ctx, req) Defensive patterns
Strategy: validation
Validate before calling
func validatePublishReq(req *csi.NodePublishVolumeRequest) error {
if req == nil { return fmt.Errorf("nil request") }
if req.ExternalID == "" { return fmt.Errorf("missing ExternalID") }
if req.TargetPath == "" { return fmt.Errorf("missing TargetPath") }
if req.StagingTargetPath == "" { return fmt.Errorf("missing StagingTargetPath") }
return nil
}
// call before client.NodePublishVolume; or simply call req.Validate() directly Type guard
func publishReqValid(req *csi.NodePublishVolumeRequest) bool {
return req != nil && req.Validate() == nil
} Try / catch
err := client.NodePublishVolume(ctx, req)
if err != nil && strings.HasPrefix(err.Error(), "validation error:") {
// local request defect; do not retry — fix the request construction
logger.Error("invalid publish request", "error", err)
return err
}
return err Prevention
- Run req.Validate() before every NodePublishVolume call to catch missing fields early
- Never send a zero-value NodePublishVolumeRequest
- Keep up to date with NodePublishVolumeRequest field changes across Nomad versions
- Unit-test the request-building code path with allocation fixtures
When it happens
Trigger: Calling client.NodePublishVolume with a NodePublishVolumeRequest whose required fields are empty — e.g. zero-value struct, missing VolumeID, missing TargetPath or StagingTargetPath, or CopyMode/ MountVolume fields left unset when required by Validate.
Common situations: Constructing NodePublishVolumeRequest by hand in tests or tooling; a Nomad bug where the hook built the request from an allocation whose volume fields were empty; API changes between Nomad versions adding new required fields that callers don't populate.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- missing volumeID
- missing stagingTargetPath
- %w: volume %q could not be found: %v
- missing secret ID
- CSI.ControllerAttachVolume: VolumeID is required
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/38fb65502e350e22.
Report an issue: GitHub.