{"record":{"id":"c69a00b04a5b8321","repo":"hashicorp/nomad","slug":"snapshot-cannot-be-nil","errorCode":null,"errorMessage":"snapshot cannot be nil","messagePattern":"snapshot cannot be nil","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/csi_endpoint.go","lineNumber":1614,"sourceCode":"\taclObj, err := v.srv.ResolveACL(args)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif !allowVolume(aclObj, args.RequestNamespace()) || !aclObj.AllowPluginRead() {\n\t\treturn structs.ErrPermissionDenied\n\t}\n\n\tstate, err := v.srv.fsm.State().Snapshot()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tmethod := \"ClientCSI.ControllerCreateSnapshot\"\n\tvar mErr multierror.Error\n\tfor _, snap := range args.Snapshots {\n\t\tif snap == nil {\n\t\t\t// we intentionally don't multierror here because we're in a weird state\n\t\t\treturn fmt.Errorf(\"snapshot cannot be nil\")\n\t\t}\n\n\t\tvol, err := state.CSIVolumeByID(nil, args.RequestNamespace(), snap.SourceVolumeID)\n\t\tif err != nil {\n\t\t\tmultierror.Append(&mErr, fmt.Errorf(\"error querying volume %q: %v\", snap.SourceVolumeID, err))\n\t\t\tcontinue\n\t\t}\n\t\tif vol == nil {\n\t\t\tmultierror.Append(&mErr, fmt.Errorf(\"no such volume %q\", snap.SourceVolumeID))\n\t\t\tcontinue\n\t\t}\n\n\t\tpluginID := snap.PluginID\n\t\tif pluginID == \"\" {\n\t\t\tpluginID = vol.PluginID\n\t\t}\n\n\t\tplugin, err := state.CSIPluginByID(nil, pluginID)","sourceCodeStart":1596,"sourceCodeEnd":1632,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/csi_endpoint.go#L1596-L1632","documentation":"CreateSnapshot returns this when an element of args.Snapshots is nil. The loop iterates over all requested snapshots and returns immediately (deliberately not appending to the multierror, per the inline comment, because the request state is 'weird') if any entry is nil. It is a caller-side payload validation failure of the CreateSnapshot RPC.","triggerScenarios":"A caller (UI, API client, or job spec tooling) submits a CSISnapshotCreateRequest whose Snapshots slice contains a nil *CSISnapshot entry — e.g. constructing the slice with a fixed length and leaving one index unset, or unmarshalling JSON array entries of `null`.","commonSituations":"Batch snapshot scripts that append only some snapshots to a pre-sized slice (`make([]*structs.CSISnapshot, n)` and a partial loop); JSON payloads like `{\"Snapshots\":[null]}`; a deserialization bug or an upstream wrapper that passes nil through for skipped entries.","solutions":["Fix the caller to only populate non-nil snapshot entries before sending CSISnapshotCreateRequest","Check the code that builds args.Snapshots for a mismatch between slice length and initialized elements (pre-sized slice with partial appends)","Validate/de-duplicate the payload server-side-adjacent logic: filter nil entries before calling CreateSnapshot","If the request came from JSON, fix the payload so no `null` elements appear in the Snapshots array"],"exampleFix":"// before\nsnaps := make([]*structs.CSISnapshot, 3)\nsnaps[0] = ...; snaps[1] = ... // snaps[2] stays nil\n// after\nsnaps := []*structs.CSISnapshot{}\nfor _, s := range desired { if s != nil { snaps = append(snaps, s) } }","handlingStrategy":"validation","validationCode":"func sanitizeSnapshots(in []*structs.CSISnapshot) []*structs.CSISnapshot {\n    out := in[:0]\n    for _, s := range in { if s != nil && s.SourceVolumeID != \"\" { out = append(out, s) } }\n    return out\n}","typeGuard":"func validSnapshot(s *structs.CSISnapshot) bool { return s != nil && s.SourceVolumeID != \"\" }","tryCatchPattern":"if err := createSnapshot(req); err != nil {\n    if strings.Contains(err.Error(), \"snapshot cannot be nil\") {\n        return fmt.Errorf(\"request contained nil snapshot entry; fix payload: %w\", err)\n    }\n    return err\n}","preventionTips":["Never build snapshot slices with make(len) + partial assignment; use append only","Validate JSON payloads against a schema rejecting null array elements","Unit-test batch request builders with edge cases that can emit nil entries"],"tags":["nomad","csi","snapshot","nil-pointer","request-validation"],"backgroundTag":"nil-request-element","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"}