{"record":{"id":"42ef8ffddd9de9d4","repo":"hashicorp/nomad","slug":"volume-row-conversion-error-s","errorCode":null,"errorMessage":"volume row conversion error: %s","messagePattern":"volume row conversion error: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"nomad/state/state_store.go","lineNumber":2960,"sourceCode":"\n// CSIVolumeDeregister removes the volume from the server\nfunc (s *StateStore) CSIVolumeDeregister(index uint64, namespace string, ids []string, force bool) error {\n\ttxn := s.db.WriteTxnMsgT(structs.CSIVolumeDeregisterRequestType, index)\n\tdefer txn.Abort()\n\n\tfor _, id := range ids {\n\t\texisting, err := txn.First(TableCSIVolumes, \"id\", namespace, id)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"volume lookup failed: %s: %v\", id, err)\n\t\t}\n\n\t\tif existing == nil {\n\t\t\treturn fmt.Errorf(\"volume not found: %s\", id)\n\t\t}\n\n\t\tvol, ok := existing.(*structs.CSIVolume)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"volume row conversion error: %s\", id)\n\t\t}\n\n\t\t// The common case for a volume deregister is when the volume is\n\t\t// unused, but we can also let an operator intervene in the case where\n\t\t// allocations have been stopped but claims can't be freed because\n\t\t// ex. the plugins have all been removed.\n\t\tif vol.InUse() {\n\t\t\tif !force || !s.volSafeToForce(txn, vol) {\n\t\t\t\treturn fmt.Errorf(\"volume in use: %s\", id)\n\t\t\t}\n\t\t}\n\n\t\tif err = txn.Delete(TableCSIVolumes, existing); err != nil {\n\t\t\treturn fmt.Errorf(\"volume delete failed: %s: %v\", id, err)\n\t\t}\n\t}\n\n\tif err := txn.Insert(\"index\", &IndexEntry{TableCSIVolumes, index}); err != nil {","sourceCodeStart":2942,"sourceCodeEnd":2978,"githubUrl":"https://github.com/hashicorp/nomad/blob/482b49bf1aec006f089bcfc7e632d8f6ac303e5e/nomad/state/state_store.go#L2942-L2978","documentation":"During CSIVolumeDeregister, the row found for the requested volume cannot be type-asserted to *structs.CSIVolume. This mirrors error 2600 but on the deregister path, and includes the volume ID for diagnosis. It signals internal state corruption or a cross-version row-type mismatch, not a user error.","triggerScenarios":"nomad volume deregister against a volume table row stored with an unexpected type — corrupted state store, snapshot from an incompatible Nomad version, or patched builds inserting foreign structs.","commonSituations":"Cross-version raft snapshot restore; manual state tampering; storage corruption after crash.","solutions":["Verify all servers run the same Nomad version and snapshots match that version","Attempt `nomad volume status <id>` to see whether reads also fail","If the row is unusable, purge the state store entry by restoring a clean snapshot, or force-delete via operator tooling","Report to HashiCorp with logs and snapshot info — internal invariant failure"],"exampleFix":"// before: message lacks the offending type\nreturn fmt.Errorf(\"volume row conversion error: %s\", id)\n// after\nreturn fmt.Errorf(\"volume row conversion error: %s: got %T\", id, existing)","handlingStrategy":"try-catch","validationCode":"// Ensure the volume reads cleanly via the API before deregistering\nvol, _, err := client.CSIVolumes().Get(volumeID, nil)\nif err != nil || vol == nil {\n\treturn fmt.Errorf(\"volume %s unreadable: %w\", volumeID, err)\n}","typeGuard":"func asCSIVolume(row interface{}) (*structs.CSIVolume, bool) {\n\tv, ok := row.(*structs.CSIVolume)\n\treturn v, ok\n}","tryCatchPattern":"if err := deregisterVolume(volID); err != nil && strings.Contains(err.Error(), \"volume row conversion error\") {\n\t// internal corruption: escalate to operator, do not retry\n}","preventionTips":["Keep cluster and snapshots on matching Nomad versions","Never manually mutate server state files","Alert on any 'row conversion' errors in server logs","Restore only known-good snapshots"],"tags":["go","state-store","csi","nomad","corruption"],"backgroundTag":"csi-volume-row-corruption","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"}