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

  1. Populate all required fields (ExternalID, TargetPath, StagingTargetPath, and required capability fields) before calling
  2. Call req.Validate() yourself beforehand to get the precise missing-field message
  3. Check how the task runner builds the request; trace which allocation volume field is empty
  4. 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

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


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