{"record":{"id":"0a41f4567c984f5a","repo":"hyperledger/fabric","slug":"unexpected-call-boot-kv-hashes-are-persisted-only","errorCode":null,"errorMessage":"unexpected call. Boot KV Hashes are persisted only for the data imported from snapshot","messagePattern":"unexpected call\\. Boot KV Hashes are persisted only for the data imported from snapshot","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/ledger/pvtdatastorage/store.go","lineNumber":760,"sourceCode":"\t\t\treturn nil, err\n\t\t}\n\n\t\t// for each transaction which misses private data, make an entry in missingBlockPvtDataInfo\n\t\tfor index, isSet := bitmap.NextSet(0); isSet; index, isSet = bitmap.NextSet(index + 1) {\n\t\t\ttxNum := uint64(index)\n\t\t\tmissingPvtDataInfo.Add(missingDataKey.blkNum, txNum, missingDataKey.ns, missingDataKey.coll)\n\t\t}\n\t}\n\n\treturn missingPvtDataInfo, nil\n}\n\n// FetchBootKVHashes returns the KVHashes from the data that was loaded from a snapshot at the time of\n// bootstrapping. This function returns an error if the supplied blkNum is greater than the last block\n// number in the booting snapshot\nfunc (s *Store) FetchBootKVHashes(blkNum, txNum uint64, ns, coll string) (map[string][]byte, error) {\n\tif s.bootsnapshotInfo.createdFromSnapshot && blkNum > s.bootsnapshotInfo.lastBlockInSnapshot {\n\t\treturn nil, errors.New(\n\t\t\t\"unexpected call. Boot KV Hashes are persisted only for the data imported from snapshot\",\n\t\t)\n\t}\n\tencVal, err := s.db.Get(\n\t\tencodeBootKVHashesKey(\n\t\t\t&bootKVHashesKey{\n\t\t\t\tblkNum: blkNum,\n\t\t\t\ttxNum:  txNum,\n\t\t\t\tns:     ns,\n\t\t\t\tcoll:   coll,\n\t\t\t},\n\t\t),\n\t)\n\tif err != nil || encVal == nil {\n\t\treturn nil, err\n\t}\n\tbootKVHashes, err := decodeBootKVHashesVal(encVal)\n\tif err != nil {","sourceCodeStart":742,"sourceCodeEnd":778,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/ledger/pvtdatastorage/store.go#L742-L778","documentation":"FetchBootKVHashes returns boot-time KV hashes only for data that was imported from a snapshot at bootstrap. If the store was not created from a snapshot, or the requested block is past the snapshot's last block, the call is considered an API misuse and this error is returned. Boot KV hashes simply do not exist for such blocks.","triggerScenarios":"Calling FetchBootKVHashes(blkNum, ...) on a store whose bootsnapshotInfo.createdFromSnapshot is false, or with blkNum > bootsnapshotInfo.lastBlockInSnapshot — e.g. reconciling/purging logic probing blocks outside the boot snapshot range.","commonSituations":"Peers bootstrapped from genesis (not a snapshot) whose pvtdata reconciliation path still probes boot KV hashes; reconciliation jobs scanning past the snapshot boundary; mixing snapshot-booted and genesis-booted peers in the same operational runbook.","solutions":["Check store.BootsnapshotInfo().CreatedFromSnapshot before calling; skip boot-KV-hash logic for genesis-booted stores.","Clamp blkNum to lastBlockInSnapshot before calling FetchBootKVHashes.","Handle the error gracefully — treat 'no boot hashes' as an empty result rather than a fatal condition in reconciliation code.","If snapshot bootstrap was intended, verify the snapshot import completed and the correct ledger config was used at startup."],"exampleFix":"// before\nhashes, err := store.FetchBootKVHashes(blkNum, txNum, ns, coll)\n// after\ninfo := store.BootsnapshotInfo()\nif !info.CreatedFromSnapshot || blkNum > info.LastBlockInSnapshot {\n    return nil, nil // no boot KV hashes exist for this range\n}\nhashes, err := store.FetchBootKVHashes(blkNum, txNum, ns, coll)","handlingStrategy":"type-guard","validationCode":"info := store.BootsnapshotInfo()\ncanFetchBoot := info != nil && info.CreatedFromSnapshot && blkNum <= info.LastBlockInSnapshot","typeGuard":"func bootKVHashesAvailable(info *BootsnapshotInfo, blkNum uint64) bool {\n    return info != nil && info.CreatedFromSnapshot && blkNum <= info.LastBlockInSnapshot\n}","tryCatchPattern":"if !bootKVHashesAvailable(store.BootsnapshotInfo(), blkNum) {\n    return nil, nil // no boot hashes for this block; skip\n}\nhashes, err := store.FetchBootKVHashes(blkNum, txNum, ns, coll)\nif err != nil {\n    if strings.Contains(err.Error(), \"unexpected call\") {\n        return nil, nil\n    }\n    return nil, err\n}","preventionTips":["Record whether the store was bootstrapped from a snapshot in your node metadata.","Clamp reconciliation scans to the boot snapshot's last block.","Treat absence of boot KV hashes as a normal empty result in reconciliation logic."],"tags":["ledger","private-data","snapshot","api-misuse"],"backgroundTag":"snapshot-scope-violation","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}