{"record":{"id":"41d77e5843a4542a","repo":"hashicorp/nomad","slug":"could-not-detach-from-controller-v","errorCode":null,"errorMessage":"could not detach from controller: %v","messagePattern":"could not detach from controller: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/csi_endpoint.go","lineNumber":970,"sourceCode":"\n\t\t\treturn fmt.Errorf(\"missing external node ID: %v\", err)\n\t\t}\n\t\tclaim.ExternalNodeID = externalNodeID\n\t}\n\n\treq := &cstructs.ClientCSIControllerDetachVolumeRequest{\n\t\tVolumeID:        vol.RemoteID(),\n\t\tClientCSINodeID: claim.ExternalNodeID,\n\t\tSecrets:         vol.Secrets,\n\t}\n\treq.PluginID = vol.PluginID\n\n\terr = v.serializedControllerRPC(vol.PluginID, func() error {\n\t\treturn v.srv.RPC(\"ClientCSI.ControllerDetachVolume\", req,\n\t\t\t&cstructs.ClientCSIControllerDetachVolumeResponse{})\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not detach from controller: %v\", err)\n\t}\n\n\tv.logger.Trace(\"controller detach complete\", \"vol\", vol.ID)\n\tclaim.State = structs.CSIVolumeClaimStateReadyToFree\n\treturn v.checkpointClaim(vol, claim)\n}\n\n// lookupExternalNodeID gets the CSI plugin's ID for a node.  we look it up in\n// the volume's claims first because it's possible the client has been stopped\n// and GC'd by this point, so looking there is the last resort.\nfunc (v *CSIVolume) lookupExternalNodeID(vol *structs.CSIVolume, claim *structs.CSIVolumeClaim) (string, error) {\n\tfor _, rClaim := range vol.ReadClaims {\n\t\tif rClaim.NodeID == claim.NodeID && rClaim.ExternalNodeID != \"\" {\n\t\t\treturn rClaim.ExternalNodeID, nil\n\t\t}\n\t}\n\tfor _, wClaim := range vol.WriteClaims {\n\t\tif wClaim.NodeID == claim.NodeID && wClaim.ExternalNodeID != \"\" {","sourceCodeStart":952,"sourceCodeEnd":988,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/csi_endpoint.go#L952-L988","documentation":"Fires in controllerUnpublishVolume when the ClientCSI.ControllerDetachVolume RPC to the plugin's controller fails; Nomad could not tell the storage backend to detach, so the claim stays published and the volume cannot be freed.","triggerScenarios":"Thrown at nomad/csi_endpoint.go:970 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Check the CSI plugin controller logs for the underlying failure","Ensure at least one controller instance of the plugin is healthy","Retry unpublish; the serialized controller RPC will re-attempt detach"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"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"}