{"record":{"id":"c4a6827b05206475","repo":"hashicorp/nomad","slug":"missing-volumeid-c4a682","errorCode":null,"errorMessage":"missing volumeID","messagePattern":"missing volumeID","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/csi/client.go","lineNumber":831,"sourceCode":"\t\tcase codes.FailedPrecondition:\n\t\t\terr = fmt.Errorf(\"volume %q does not have MULTI_NODE volume capability: %v\",\n\t\t\t\treq.ExternalID, err)\n\t\tcase codes.Internal:\n\t\t\terr = fmt.Errorf(\"node plugin returned an internal error, check the plugin allocation logs for more information: %v\", err)\n\t\t}\n\t}\n\n\treturn err\n}\n\nfunc (c *client) NodeUnstageVolume(ctx context.Context, volumeID string, stagingTargetPath string, opts ...grpc.CallOption) error {\n\tif err := c.ensureConnected(ctx); err != nil {\n\t\treturn err\n\t}\n\t// These errors should not be returned during production use but exist as aids\n\t// during Nomad development\n\tif volumeID == \"\" {\n\t\treturn fmt.Errorf(\"missing volumeID\")\n\t}\n\tif stagingTargetPath == \"\" {\n\t\treturn fmt.Errorf(\"missing stagingTargetPath\")\n\t}\n\n\treq := &csipbv1.NodeUnstageVolumeRequest{\n\t\tVolumeId:          volumeID,\n\t\tStagingTargetPath: stagingTargetPath,\n\t}\n\n\t// NodeUnstageVolume's response contains no extra data. If err == nil, we were\n\t// successful.\n\t_, err := c.nodeClient.NodeUnstageVolume(ctx, req, opts...)\n\tif err != nil {\n\t\tcode := status.Code(err)\n\t\tswitch code {\n\t\tcase codes.NotFound:\n\t\t\terr = fmt.Errorf(\"%w: volume %q could not be found: %v\",","sourceCodeStart":813,"sourceCodeEnd":849,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/plugins/csi/client.go#L813-L849","documentation":"NodeUnstageVolume in the Nomad CSI client wrapper returns this sentinel error before ever issuing a gRPC call when the volumeID argument is an empty string. The source comments that these errors 'should not be returned during production use but exist as aids during Nomad development' — they guard against the Nomad CSI plugin's own internal caller passing empty identifiers to the underlying CSI NodeUnstageVolume RPC. It is a fail-fast developer aid, not a remote failure.","triggerScenarios":"Calling client.NodeUnstageVolume(ctx, \"\", stagingTargetPath) with an empty volumeID, typically from a task-runner hook or volume unmounting path where the CSI volume's ExternalID was never populated or was lost during a restore/checkpoint.","commonSituations":"Nomad development/testing of new volume integration code; volumes whose registration records were corrupted or partially restored after agent restart; custom tooling invoking the CSI client directly with unverified volume state.","solutions":["Verify the volume record (csivolumes) has a valid ExternalID before calling NodeUnstageVolume","Log and inspect the caller's volume state; the empty ID usually indicates an upstream Nomad bug, not user misconfiguration","Check the Nomad version for known bugs in volume claim untracking and upgrade","Skip/report the call: an empty volumeID means there is nothing to unstage, so no cleanup is lost"],"exampleFix":"// before\ncsi.Client.NodeUnstageVolume(ctx, vol.ID, stagingPath)\n// after\nif vol.ID == \"\" {\n\tlogger.Warn(\"skipping unstage: volume ID already empty\")\n\treturn nil\n}\ncsi.Client.NodeUnstageVolume(ctx, vol.ID, stagingPath)","handlingStrategy":"validation","validationCode":"if volumeID == \"\" {\n\treturn fmt.Errorf(\"cannot unstage: volumeID is empty; check the volume claim record\")\n}\n// proceed to client.NodeUnstageVolume(ctx, volumeID, stagingTargetPath)","typeGuard":"func hasVolumeID(id string) bool { return strings.TrimSpace(id) != \"\" }","tryCatchPattern":"err := client.NodeUnstageVolume(ctx, volumeID, stagingPath)\nif err != nil && strings.Contains(err.Error(), \"missing volumeID\") {\n\t// developer-aid error: log volume state and skip; nothing to unstage\n\tlogger.Warn(\"unstage skipped: empty volumeID\", \"claim\", claim)\n\treturn nil\n}\nreturn err","preventionTips":["Never call NodeUnstageVolume without a non-empty ExternalID from the volume claim record","Validate volume state after agent restore/checkpoint before cleanup hooks run","Write tests covering unstage with partially populated claim structs"],"tags":["csi","go","nomad","volume-management","empty-argument"],"backgroundTag":"missing-required-argument","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"}