hashicorp/nomad · error

snapshot %q already exists but is incompatible with volume I

Error message

snapshot %q already exists but is incompatible with volume ID %q: %v

What it means

ControllerCreateSnapshot translates gRPC status codes from the CSI plugin's CreateSnapshot RPC into user-readable errors. codes.AlreadyExists per the CSI spec means a snapshot with the requested name already exists, but it was created from a different source volume than req.VolumeID, making it incompatible. Nomad surfaces this so the caller knows the name collision is with a snapshot of a different volume.

Source

Thrown at plugins/csi/client.go:646

	if err := c.ensureConnected(ctx); err != nil {
		return nil, err
	}

	err := req.Validate()
	if err != nil {
		return nil, err
	}
	creq := req.ToCSIRepresentation()
	resp, err := c.controllerClient.CreateSnapshot(ctx, creq, opts...)

	// these standard gRPC error codes are overloaded with CSI-specific
	// meanings, so translate them into user-understandable terms
	// https://github.com/container-storage-interface/spec/blob/master/spec.md#createsnapshot-errors
	if err != nil {
		code := status.Code(err)
		switch code {
		case codes.AlreadyExists:
			return nil, fmt.Errorf(
				"snapshot %q already exists but is incompatible with volume ID %q: %v",
				req.Name, req.VolumeID, err)
		case codes.Aborted:
			return nil, fmt.Errorf(
				"snapshot %q is already pending: %v",
				req.Name, err)
		case codes.ResourceExhausted:
			return nil, fmt.Errorf(
				"storage provider does not have enough space for this snapshot: %v", err)
		case codes.Internal:
			return nil, fmt.Errorf(
				"controller plugin returned an internal error, check the plugin allocation logs for more information: %v", err)
		}
		return nil, err
	}

	snap := resp.GetSnapshot()
	return &ControllerCreateSnapshotResponse{

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Choose a new unique snapshot name (req.Name) for the CreateSnapshot call.
  2. Delete the pre-existing incompatible snapshot first via ControllerDeleteSnapshot with its snapshot ID.
  3. Verify the req.VolumeID is the intended source volume; you may be pointing at the wrong CSI volume.
  4. List existing snapshots (ControllerListSnapshots) to find the colliding name and its source volume.

Example fix

// before
_, err := c.ControllerCreateSnapshot(ctx, &ControllerCreateSnapshotRequest{
  SnapshotID: "daily-backup",
  VolumeID:   volB.ID,
})
// after: unique, volume-scoped name
_, err := c.ControllerCreateSnapshot(ctx, &ControllerCreateSnapshotRequest{
  SnapshotID: fmt.Sprintf("%s-daily-backup", volB.ID),
  VolumeID:   volB.ID,
})
Defensive patterns

Strategy: validation

Validate before calling

func snapshotNameAvailable(ctx context.Context, c *client, name, volumeID string) error {
  resp, err := c.ControllerListSnapshots(ctx, &ControllerListSnapshotsRequest{})
  if err != nil { return err }
  for _, s := range resp.Snapshots {
    if s.ID == name || s.SourceVolumeID != volumeID && strings.Contains(s.ID, name) {
      return fmt.Errorf("snapshot name %q already exists for different volume", name)
    }
  }
  return nil
}

Try / catch

_, err := c.ControllerCreateSnapshot(ctx, req)
if err != nil && strings.Contains(err.Error(), "already exists but is incompatible") {
  return fmt.Errorf("pick a new snapshot name for volume %s: %w", req.VolumeID, err)
}

Prevention

When it happens

Trigger: Calling ControllerCreateSnapshot with req.Name matching an existing snapshot on the storage backend whose source volume ID differs from req.VolumeID; the plugin returns gRPC AlreadyExists and this message is built at client.go:646.

Common situations: Reusing a snapshot name from a previous job/volume; two Nomad jobs create snapshots with the same name against the same storage array; stale snapshot left over after volume re-provisioning; case/ID drift between Nomad volume IDs and backend volume handles.

Related errors


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