hashicorp/nomad · error

CSI.ControllerListSnapshot: plugin returned an invalid entry

Error message

CSI.ControllerListSnapshot: plugin returned an invalid entry

What it means

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.

Source

Thrown at client/csi_endpoint.go:479

	ctx, cancelFn := c.requestContext()
	defer cancelFn()

	// CSI ControllerListSnapshots errors for timeout, codes.Unavailable and
	// codes.ResourceExhausted are retried; all other errors are fatal.
	cresp, err := plugin.ControllerListSnapshots(ctx, csiReq,
		grpc_retry.WithPerRetryTimeout(CSIPluginRequestTimeout),
		grpc_retry.WithMax(3),
		grpc_retry.WithBackoff(grpc_retry.BackoffExponential(100*time.Millisecond)))
	if err != nil {
		return fmt.Errorf("CSI.ControllerListSnapshots: %v", err)
	}

	resp.NextToken = cresp.NextToken
	resp.Entries = []*nstructs.CSISnapshot{}

	for _, entry := range cresp.Entries {
		if entry.Snapshot == nil {
			return fmt.Errorf("CSI.ControllerListSnapshot: plugin returned an invalid entry")
		}
		snap := &nstructs.CSISnapshot{
			ID:                     entry.Snapshot.ID,
			ExternalSourceVolumeID: entry.Snapshot.SourceVolumeID,
			SizeBytes:              entry.Snapshot.SizeBytes,
			CreateTime:             entry.Snapshot.CreateTime,
			IsReady:                entry.Snapshot.IsReady,
			PluginID:               req.PluginID,
		}
		resp.Entries = append(resp.Entries, snap)
		if req.MaxEntries != 0 && int32(len(resp.Entries)) == req.MaxEntries {
			break
		}
	}

	return nil
}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Report the malformed list response to the plugin author
  2. Upgrade the CSI plugin to a release fixing list response construction
  3. Validate the plugin directly with `csc controller list-snapshots` to reproduce and confirm
  4. Work around by filtering list queries (volume ID / snapshot ID / next_token) to skip malformed entries

Example fix

// before (plugin side)
for _, s := range snaps {
    out.Entries = append(out.Entries, &csi.ListSnapshotsResponse_Entry{}) // snapshot forgotten
}
// after
for _, s := range snaps {
    out.Entries = append(out.Entries, &csi.ListSnapshotsResponse_Entry{Snapshot: toProto(s)})
}
Defensive patterns

Strategy: validation

Validate before calling

// Sanity-check list results at the plugin boundary before automation consumes them:
for _, e := range cresp.Entries {
    if e.Snapshot == nil {
        return fmt.Errorf("plugin %s returned a list entry without a snapshot", pluginName)
    }
}

Type guard

func hasSnapshot(e *csi.ListSnapshotsResponse_Entry) bool {
    return e != nil && e.Snapshot != nil
}

Try / catch

// Note the singular 'ListSnapshot' typo in the message when matching
if err := c.ControllerListSnapshots(req, resp); err != nil {
    if strings.Contains(err.Error(), "plugin returned an invalid entry") {
        // treat as plugin bug: upgrade or report to vendor
    }
}

Prevention

When it happens

Trigger: The plugin's ListSnapshots response contains an Entries element with a nil Snapshot field while gRPC status was OK.

Common situations: 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.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/33eb5c0a6ef93f9f. Report an issue: GitHub.