{"record":{"id":"2bf6cde955eb20b0","repo":"hashicorp/nomad","slug":"snapshot-q-could-not-be-deleted-because-it-is-in","errorCode":null,"errorMessage":"snapshot %q could not be deleted because it is in use: %v","messagePattern":"snapshot %q could not be deleted because it is in use: (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/csi/client.go","lineNumber":694,"sourceCode":"\tif err := c.ensureConnected(ctx); err != nil {\n\t\treturn err\n\t}\n\n\terr := req.Validate()\n\tif err != nil {\n\t\treturn err\n\t}\n\tcreq := req.ToCSIRepresentation()\n\t_, err = c.controllerClient.DeleteSnapshot(ctx, creq, opts...)\n\n\t// these standard gRPC error codes are overloaded with CSI-specific\n\t// meanings, so translate them into user-understandable terms\n\t// https://github.com/container-storage-interface/spec/blob/master/spec.md#deletesnapshot-errors\n\tif err != nil {\n\t\tcode := status.Code(err)\n\t\tswitch code {\n\t\tcase codes.FailedPrecondition:\n\t\t\treturn fmt.Errorf(\n\t\t\t\t\"snapshot %q could not be deleted because it is in use: %v\",\n\t\t\t\treq.SnapshotID, err)\n\t\tcase codes.Aborted:\n\t\t\treturn fmt.Errorf(\"snapshot %q has a pending operation: %v\", req.SnapshotID, err)\n\t\tcase codes.Internal:\n\t\t\treturn fmt.Errorf(\n\t\t\t\t\"controller plugin returned an internal error, check the plugin allocation logs for more information: %v\", err)\n\t\t}\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc (c *client) ControllerListSnapshots(ctx context.Context, req *ControllerListSnapshotsRequest, opts ...grpc.CallOption) (*ControllerListSnapshotsResponse, error) {\n\tif err := c.ensureConnected(ctx); err != nil {\n\t\treturn nil, err\n\t}","sourceCodeStart":676,"sourceCodeEnd":712,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/plugins/csi/client.go#L676-L712","documentation":"ControllerDeleteSnapshot translates gRPC codes.FailedPrecondition from the plugin's DeleteSnapshot RPC per the CSI spec: the snapshot exists but cannot be deleted because something depends on it (e.g. a volume created/restored from it, or an in-progress clone). Nomad surfaces this as 'in use' so the operator knows the delete is blocked by a dependency, not a transient fault.","triggerScenarios":"Calling ControllerDeleteSnapshot with req.SnapshotID for a snapshot that is the source of an existing volume, an active restore, or a dependent clone, and the plugin returns codes.FailedPrecondition (client.go:694).","commonSituations":"Trying to prune snapshots still referenced by running CSI volumes; a restore job is consuming the snapshot; retention policy deleting snapshots linked to downstream copies on the array; cloud provider blocks deleting a snapshot used by another disk.","solutions":["Find and delete (or finish) the dependent volumes/restores created from this snapshot before retrying.","Reorder the cleanup workflow: delete dependent volumes first, then the snapshot.","Exclude in-use snapshots from the retention/pruning list (check snapshot dependencies via backend tooling).","Confirm req.SnapshotID is correct — a stale/wrong ID may point at a snapshot that is genuinely in use."],"exampleFix":"// before: blind prune\nfor _, s := range snapshots { deleteSnapshot(s.ID) }\n// after: skip snapshots with dependents\nfor _, s := range snapshots {\n  if !hasDependentVolumes(s.ID) { deleteSnapshot(s.ID) }\n}","handlingStrategy":"validation","validationCode":"func canDeleteSnapshot(ctx context.Context, c *client, snapID string) error {\n  vols, err := listVolumesSourcedFromSnapshot(ctx, c, snapID) // backend/driver API\n  if err != nil { return err }\n  if len(vols) > 0 {\n    return fmt.Errorf(\"snapshot %s in use by %d volumes\", snapID, len(vols))\n  }\n  return nil\n}","typeGuard":null,"tryCatchPattern":"err := c.ControllerDeleteSnapshot(ctx, req)\nif err != nil && strings.Contains(err.Error(), \"in use\") {\n  log.Printf(\"deferring delete of %s: dependent resources exist\", req.SnapshotID)\n  return ErrSnapshotInUse // handle in a later cleanup pass\n}","preventionTips":["Delete dependent volumes/clones before pruning their source snapshots.","Tag snapshots with dependency metadata your cleanup job can check.","Keep restores and retention windows non-overlapping."],"tags":["csi","snapshot","grpc","nomad","storage"],"backgroundTag":"resource-in-use","analyzedSha":"482b49bf1aec006f089bcfc7e632d8f6ac303e5e","analyzedAt":"2026-09-04T07:54:14.808Z","contentChangedAt":"2026-09-04T07:54:14.808Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}