hashicorp/nomad · error

no such volume

Error message

no such volume

What it means

CSIVolume.Unpublish cannot find the requested volume in the state store and returns the terse "no such volume". Unlike volAndPluginLookup this path doesn't echo the ID, but the cause is the same: no CSIVolume exists under (args.Namespace, args.VolumeID) at the time of the RPC. The unpublish is aborted without touching claims or the controller.

Source

Thrown at nomad/csi_endpoint.go:723

	if err := v.authorizeUnpublish(aclObj, args, allowVolume); err != nil {
		return err
	}

	if args.VolumeID == "" {
		return fmt.Errorf("missing volume ID")
	}
	if args.Claim == nil {
		return fmt.Errorf("missing volume claim")
	}

	ws := memdb.NewWatchSet()
	state := v.srv.fsm.State()
	vol, err := state.CSIVolumeByID(ws, args.Namespace, args.VolumeID)
	if err != nil {
		return err
	}
	if vol == nil {
		return fmt.Errorf("no such volume")
	}

	claim := args.Claim

	// we need to checkpoint when we first get the claim to ensure we've set the
	// initial "past claim" state, otherwise a client that unpublishes (skipping
	// the node unpublish b/c it's done that work) fail to get written if the
	// controller unpublish fails.
	vol = vol.Copy()
	err = v.checkpointClaim(vol, claim)
	if err != nil {
		return err
	}

	// previous checkpoints may have set the past claim state already.
	// in practice we should never see CSIVolumeClaimStateControllerDetached
	// but having an option for the state makes it easy to add a checkpoint
	// in a backwards compatible way if we need one later

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Verify with `nomad volume status -namespace <ns> <vol-id>`; if already gone, treat the unpublish as a no-op in your cleanup logic.
  2. Re-register the volume if it must remain, then retry the unpublish.
  3. Confirm the request's namespace matches the volume's registered namespace.
  4. Serialize cleanup so volume delete only runs after all unpublish RPCs complete.

Example fix

// before: unconditional unpublish in cleanup
client.CSIVolumes().Unpublish(volID, ns, opts, req)
// after: tolerate already-deregistered volumes
err := client.CSIVolumes().Unpublish(volID, ns, opts, req)
if err != nil && strings.Contains(err.Error(), "no such volume") {
    return nil // already gone; safe to skip
}
Defensive patterns

Strategy: fallback

Validate before calling

vol, _, err := client.CSIVolumes().Info(volID, nil)
if err != nil || vol == nil {
    return nil // volume already deregistered; nothing to unpublish
}

Try / catch

if err != nil && strings.Contains(err.Error(), "no such volume") {
    return nil // idempotent success: already deregistered
}

Prevention

When it happens

Trigger: Raised when state.CSIVolumeByID(ws, args.Namespace, args.VolumeID) returns nil in Unpublish: the volume was already deregistered/deleted, never existed, is in another namespace, or was deregistered concurrently between claim release and unpublish.

Common situations: Idempotent cleanup scripts unpublishing volumes that were already deregistered; raciness where `nomad volume delete` ran while a client was still unpublishing; namespace mismatch (caller authorized for a different namespace than the volume's).

Related errors


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