{"record":{"id":"f6feb8336e1fe2ae","repo":"hyperledger/fabric","slug":"failed-to-unmarshal-savepoint-data","errorCode":null,"errorMessage":"failed to unmarshal savepoint data","messagePattern":"failed to unmarshal savepoint data","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/ledger/kvledger/txmgmt/statedb/statecouchdb/couchdoc_conv.go","lineNumber":223,"sourceCode":"func encodeSavepoint(height *version.Height) (*couchDoc, error) {\n\tvar err error\n\tvar savepointDoc couchSavepointData\n\t// construct savepoint document\n\tsavepointDoc.BlockNum = height.BlockNum\n\tsavepointDoc.TxNum = height.TxNum\n\tsavepointDocJSON, err := json.Marshal(savepointDoc)\n\tif err != nil {\n\t\terr = errors.Wrap(err, \"failed to marshal savepoint data\")\n\t\tlogger.Errorf(\"%+v\", err)\n\t\treturn nil, err\n\t}\n\treturn &couchDoc{jsonValue: savepointDocJSON, attachments: nil}, nil\n}\n\nfunc decodeSavepoint(couchDoc *couchDoc) (*version.Height, error) {\n\tsavepointDoc := &couchSavepointData{}\n\tif err := json.Unmarshal(couchDoc.jsonValue, &savepointDoc); err != nil {\n\t\terr = errors.Wrap(err, \"failed to unmarshal savepoint data\")\n\t\tlogger.Errorf(\"%+v\", err)\n\t\treturn nil, err\n\t}\n\treturn &version.Height{BlockNum: savepointDoc.BlockNum, TxNum: savepointDoc.TxNum}, nil\n}\n\nfunc encodeChannelMetadata(metadataDoc *channelMetadata) (*couchDoc, error) {\n\tmetadataJSON, err := json.Marshal(metadataDoc)\n\tif err != nil {\n\t\terr = errors.Wrap(err, \"failed to marshal channel metadata\")\n\t\tlogger.Errorf(\"%+v\", err)\n\t\treturn nil, err\n\t}\n\treturn &couchDoc{jsonValue: metadataJSON, attachments: nil}, nil\n}\n\nfunc decodeChannelMetadata(couchDoc *couchDoc) (*channelMetadata, error) {\n\tmetadataDoc := &channelMetadata{}","sourceCodeStart":205,"sourceCodeEnd":241,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/ledger/kvledger/txmgmt/statedb/statecouchdb/couchdoc_conv.go#L205-L241","documentation":"decodeSavepoint parses the stored savepoint JSON document back into a version.Height. If json.Unmarshal fails, the error is wrapped with 'failed to unmarshal savepoint data' and GetLatestSavePoint fails. This indicates the saved savepoint document in CouchDB is corrupt or in an unexpected format.","triggerScenarios":"GetLatestSavePoint reads the savepoint document from CouchDB and the jsonValue bytes cannot be unmarshaled into couchSavepointData (corrupt bytes, wrong document type, truncated JSON).","commonSituations":"Manual edits to the CouchDB savepoint document, database corruption, restoring CouchDB from an incompatible backup, or another application overwriting the savepoint doc in the ledger's database.","solutions":["Inspect the savepoint document in CouchDB; restore it to valid JSON with BlockNum and TxNum fields","Restore the state database from a known-good backup","If the ledger is unusable, drop and rebuild the ledger database (full re-sync from the blockchain/orderer)"],"exampleFix":"// before (corrupt doc)\n{\"BlockNum\": \"not-a-number\"}\n// after\n{\"BlockNum\": 100, \"TxNum\": 0}","handlingStrategy":"try-catch","validationCode":"func validSavepointJSON(b []byte) bool {\n    var d struct{ BlockNum uint64; TxNum uint64 }\n    return json.Unmarshal(b, &d) == nil\n}","typeGuard":null,"tryCatchPattern":"h, err := decodeSavepoint(doc)\nif err != nil {\n    return nil, fmt.Errorf(\"savepoint document corrupt, restore from backup: %w\", err)\n}","preventionTips":["Do not modify internal savepoint documents in CouchDB","Take consistent full backups of the state database","Restrict CouchDB admin access to ledger databases"],"tags":["json","unmarshalling","savepoint","fabric-ledger","couchdb"],"backgroundTag":"json-unmarshalling-failed","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"}