hashicorp/nomad · error

missing volumeID

Error message

missing volumeID

What it means

NodeUnstageVolume in the Nomad CSI client wrapper returns this sentinel error before ever issuing a gRPC call when the volumeID argument is an empty string. The source comments that these errors 'should not be returned during production use but exist as aids during Nomad development' — they guard against the Nomad CSI plugin's own internal caller passing empty identifiers to the underlying CSI NodeUnstageVolume RPC. It is a fail-fast developer aid, not a remote failure.

Source

Thrown at plugins/csi/client.go:831

		case codes.FailedPrecondition:
			err = fmt.Errorf("volume %q does not have MULTI_NODE volume capability: %v",
				req.ExternalID, 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) NodeUnstageVolume(ctx context.Context, volumeID string, stagingTargetPath string, opts ...grpc.CallOption) error {
	if err := c.ensureConnected(ctx); err != nil {
		return err
	}
	// These errors should not be returned during production use but exist as aids
	// during Nomad development
	if volumeID == "" {
		return fmt.Errorf("missing volumeID")
	}
	if stagingTargetPath == "" {
		return fmt.Errorf("missing stagingTargetPath")
	}

	req := &csipbv1.NodeUnstageVolumeRequest{
		VolumeId:          volumeID,
		StagingTargetPath: stagingTargetPath,
	}

	// NodeUnstageVolume's response contains no extra data. If err == nil, we were
	// successful.
	_, err := c.nodeClient.NodeUnstageVolume(ctx, req, opts...)
	if err != nil {
		code := status.Code(err)
		switch code {
		case codes.NotFound:
			err = fmt.Errorf("%w: volume %q could not be found: %v",

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Verify the volume record (csivolumes) has a valid ExternalID before calling NodeUnstageVolume
  2. Log and inspect the caller's volume state; the empty ID usually indicates an upstream Nomad bug, not user misconfiguration
  3. Check the Nomad version for known bugs in volume claim untracking and upgrade
  4. Skip/report the call: an empty volumeID means there is nothing to unstage, so no cleanup is lost

Example fix

// before
csi.Client.NodeUnstageVolume(ctx, vol.ID, stagingPath)
// after
if vol.ID == "" {
	logger.Warn("skipping unstage: volume ID already empty")
	return nil
}
csi.Client.NodeUnstageVolume(ctx, vol.ID, stagingPath)
Defensive patterns

Strategy: validation

Validate before calling

if volumeID == "" {
	return fmt.Errorf("cannot unstage: volumeID is empty; check the volume claim record")
}
// proceed to client.NodeUnstageVolume(ctx, volumeID, stagingTargetPath)

Type guard

func hasVolumeID(id string) bool { return strings.TrimSpace(id) != "" }

Try / catch

err := client.NodeUnstageVolume(ctx, volumeID, stagingPath)
if err != nil && strings.Contains(err.Error(), "missing volumeID") {
	// developer-aid error: log volume state and skip; nothing to unstage
	logger.Warn("unstage skipped: empty volumeID", "claim", claim)
	return nil
}
return err

Prevention

When it happens

Trigger: Calling client.NodeUnstageVolume(ctx, "", stagingTargetPath) with an empty volumeID, typically from a task-runner hook or volume unmounting path where the CSI volume's ExternalID was never populated or was lost during a restore/checkpoint.

Common situations: Nomad development/testing of new volume integration code; volumes whose registration records were corrupted or partially restored after agent restart; custom tooling invoking the CSI client directly with unverified volume state.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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