hashicorp/nomad · error

no such volume %q

Error message

no such volume %q

What it means

CreateSnapshot appends 'no such volume %q' to the multierror when CSIVolumeByID succeeds but returns a nil volume, meaning the SourceVolumeID given in the snapshot request does not exist in Nomad's state store in the requested namespace. Each offending snapshot is skipped (continue) while remaining snapshots in the batch are still processed.

Source

Thrown at nomad/csi_endpoint.go:1623

	if err != nil {
		return err
	}

	method := "ClientCSI.ControllerCreateSnapshot"
	var mErr multierror.Error
	for _, snap := range args.Snapshots {
		if snap == nil {
			// we intentionally don't multierror here because we're in a weird state
			return fmt.Errorf("snapshot cannot be nil")
		}

		vol, err := state.CSIVolumeByID(nil, args.RequestNamespace(), snap.SourceVolumeID)
		if err != nil {
			multierror.Append(&mErr, fmt.Errorf("error querying volume %q: %v", snap.SourceVolumeID, err))
			continue
		}
		if vol == nil {
			multierror.Append(&mErr, fmt.Errorf("no such volume %q", snap.SourceVolumeID))
			continue
		}

		pluginID := snap.PluginID
		if pluginID == "" {
			pluginID = vol.PluginID
		}

		plugin, err := state.CSIPluginByID(nil, pluginID)
		if err != nil {
			multierror.Append(&mErr,
				fmt.Errorf("error querying plugin %q: %v", pluginID, err))
			continue
		}
		if plugin == nil {
			multierror.Append(&mErr, fmt.Errorf("no such plugin %q", pluginID))
			continue
		}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. List existing volumes (`nomad volume status` or GET /v1/volumes) and correct SourceVolumeID to an existing volume in the target namespace
  2. Check the namespace: the volume may exist but in another namespace — either move the snapshot request to that namespace or grant cross-namespace access
  3. Re-create/register the missing volume (nomad volume create / re-run the volume registration job) if it was deleted
  4. Inspect the returned multierror to see which specific SourceVolumeIDs failed and fix each

Example fix

// before
{"Snapshots":[{"SourceVolumeID":"db-data","PluginID":"ebs"}]}
// after: use the registered volume ID in the correct namespace
{"Snapshots":[{"SourceVolumeID":"db-data[0]","PluginID":"ebs"}]} // ID from `nomad volume status`
Defensive patterns

Strategy: validation

Validate before calling

vols, _, err := client.Volumes().List(ctx, namespace, nil)
if err != nil { return err }
known := map[string]bool{}
for _, v := range vols { known[v.ID] = true }
for _, s := range req.Snapshots {
    if !known[s.SourceVolumeID] { return fmt.Errorf("volume %q not in namespace %q", s.SourceVolumeID, namespace) }
}

Type guard

func volumeExists(id string, vols []*api.VolumeListStub) bool {
    for _, v := range vols { if v.ID == id { return true } }
    return false
}

Try / catch

if err := createSnapshot(req); err != nil {
    if me, ok := err.(*multierror.Error); ok {
        for _, e := range me.Errors {
            if strings.Contains(e.Error(), "no such volume") {
                // drop that snapshot entry from req and retry the remainder
            }
        }
    }
}

Prevention

When it happens

Trigger: Calling CreateSnapshot with a Snapshots entry whose SourceVolumeID is not a volume registered in Nomad: a typo'd volume ID, the volume was deregistered/deleted, or the volume exists in a different namespace than args.RequestNamespace() resolves to.

Common situations: Snapshot automation referencing a volume name instead of the full volume ID; volumes deleted after their job stopped; cross-namespace snapshots where the caller's token/namespace resolves differently than where the volume lives; stale config after a Nomad cluster state reset.

Related errors


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