{"record":{"id":"33eb5c0a6ef93f9f","repo":"hashicorp/nomad","slug":"csi-controllerlistsnapshot-plugin-returned-an-inv","errorCode":null,"errorMessage":"CSI.ControllerListSnapshot: plugin returned an invalid entry","messagePattern":"CSI\\.ControllerListSnapshot: plugin returned an invalid entry","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/csi_endpoint.go","lineNumber":479,"sourceCode":"\tctx, cancelFn := c.requestContext()\n\tdefer cancelFn()\n\n\t// CSI ControllerListSnapshots errors for timeout, codes.Unavailable and\n\t// codes.ResourceExhausted are retried; all other errors are fatal.\n\tcresp, err := plugin.ControllerListSnapshots(ctx, csiReq,\n\t\tgrpc_retry.WithPerRetryTimeout(CSIPluginRequestTimeout),\n\t\tgrpc_retry.WithMax(3),\n\t\tgrpc_retry.WithBackoff(grpc_retry.BackoffExponential(100*time.Millisecond)))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"CSI.ControllerListSnapshots: %v\", err)\n\t}\n\n\tresp.NextToken = cresp.NextToken\n\tresp.Entries = []*nstructs.CSISnapshot{}\n\n\tfor _, entry := range cresp.Entries {\n\t\tif entry.Snapshot == nil {\n\t\t\treturn fmt.Errorf(\"CSI.ControllerListSnapshot: plugin returned an invalid entry\")\n\t\t}\n\t\tsnap := &nstructs.CSISnapshot{\n\t\t\tID:                     entry.Snapshot.ID,\n\t\t\tExternalSourceVolumeID: entry.Snapshot.SourceVolumeID,\n\t\t\tSizeBytes:              entry.Snapshot.SizeBytes,\n\t\t\tCreateTime:             entry.Snapshot.CreateTime,\n\t\t\tIsReady:                entry.Snapshot.IsReady,\n\t\t\tPluginID:               req.PluginID,\n\t\t}\n\t\tresp.Entries = append(resp.Entries, snap)\n\t\tif req.MaxEntries != 0 && int32(len(resp.Entries)) == req.MaxEntries {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":461,"sourceCodeEnd":497,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/client/csi_endpoint.go#L461-L497","documentation":"In ControllerListSnapshots, after a successful plugin call each entry in cresp.Entries must contain a non-nil Snapshot. If an entry has a nil Snapshot, Nomad returns this error instead of dereferencing nil. Note the message says 'ListSnapshot' (singular) — a known typo — but it originates from the list-snapshots path. It indicates the CSI plugin returned a malformed response.","triggerScenarios":"The plugin's ListSnapshots response contains an Entries element with a nil Snapshot field while gRPC status was OK.","commonSituations":"Buggy plugin versions emitting partially-populated entries (e.g. deleted snapshots represented as empty entries); plugin/vendor SDK changes altering list response serialization; plugins built against mismatched CSI spec versions.","solutions":["Report the malformed list response to the plugin author","Upgrade the CSI plugin to a release fixing list response construction","Validate the plugin directly with `csc controller list-snapshots` to reproduce and confirm","Work around by filtering list queries (volume ID / snapshot ID / next_token) to skip malformed entries"],"exampleFix":"// before (plugin side)\nfor _, s := range snaps {\n    out.Entries = append(out.Entries, &csi.ListSnapshotsResponse_Entry{}) // snapshot forgotten\n}\n// after\nfor _, s := range snaps {\n    out.Entries = append(out.Entries, &csi.ListSnapshotsResponse_Entry{Snapshot: toProto(s)})\n}","handlingStrategy":"validation","validationCode":"// Sanity-check list results at the plugin boundary before automation consumes them:\nfor _, e := range cresp.Entries {\n    if e.Snapshot == nil {\n        return fmt.Errorf(\"plugin %s returned a list entry without a snapshot\", pluginName)\n    }\n}","typeGuard":"func hasSnapshot(e *csi.ListSnapshotsResponse_Entry) bool {\n    return e != nil && e.Snapshot != nil\n}","tryCatchPattern":"// Note the singular 'ListSnapshot' typo in the message when matching\nif err := c.ControllerListSnapshots(req, resp); err != nil {\n    if strings.Contains(err.Error(), \"plugin returned an invalid entry\") {\n        // treat as plugin bug: upgrade or report to vendor\n    }\n}","preventionTips":["Test plugin list-snapshots output with csc before production","Pin plugin versions known to emit well-formed list entries","Filter listings by volume/snapshot ID to limit malformed responses","Report nil-entry responses to the plugin vendor promptly"],"tags":["csi","grpc","snapshot","plugin","nil-check"],"backgroundTag":"csi-plugin-invalid-response","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"}