hyperledger/fabric · error

failed to unmarshal savepoint data

Error message

failed to unmarshal savepoint data

What it means

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.

Source

Thrown at core/ledger/kvledger/txmgmt/statedb/statecouchdb/couchdoc_conv.go:223

func encodeSavepoint(height *version.Height) (*couchDoc, error) {
	var err error
	var savepointDoc couchSavepointData
	// construct savepoint document
	savepointDoc.BlockNum = height.BlockNum
	savepointDoc.TxNum = height.TxNum
	savepointDocJSON, err := json.Marshal(savepointDoc)
	if err != nil {
		err = errors.Wrap(err, "failed to marshal savepoint data")
		logger.Errorf("%+v", err)
		return nil, err
	}
	return &couchDoc{jsonValue: savepointDocJSON, attachments: nil}, nil
}

func decodeSavepoint(couchDoc *couchDoc) (*version.Height, error) {
	savepointDoc := &couchSavepointData{}
	if err := json.Unmarshal(couchDoc.jsonValue, &savepointDoc); err != nil {
		err = errors.Wrap(err, "failed to unmarshal savepoint data")
		logger.Errorf("%+v", err)
		return nil, err
	}
	return &version.Height{BlockNum: savepointDoc.BlockNum, TxNum: savepointDoc.TxNum}, nil
}

func encodeChannelMetadata(metadataDoc *channelMetadata) (*couchDoc, error) {
	metadataJSON, err := json.Marshal(metadataDoc)
	if err != nil {
		err = errors.Wrap(err, "failed to marshal channel metadata")
		logger.Errorf("%+v", err)
		return nil, err
	}
	return &couchDoc{jsonValue: metadataJSON, attachments: nil}, nil
}

func decodeChannelMetadata(couchDoc *couchDoc) (*channelMetadata, error) {
	metadataDoc := &channelMetadata{}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Inspect the savepoint document in CouchDB; restore it to valid JSON with BlockNum and TxNum fields
  2. Restore the state database from a known-good backup
  3. If the ledger is unusable, drop and rebuild the ledger database (full re-sync from the blockchain/orderer)

Example fix

// before (corrupt doc)
{"BlockNum": "not-a-number"}
// after
{"BlockNum": 100, "TxNum": 0}
Defensive patterns

Strategy: try-catch

Validate before calling

func validSavepointJSON(b []byte) bool {
    var d struct{ BlockNum uint64; TxNum uint64 }
    return json.Unmarshal(b, &d) == nil
}

Try / catch

h, err := decodeSavepoint(doc)
if err != nil {
    return nil, fmt.Errorf("savepoint document corrupt, restore from backup: %w", err)
}

Prevention

When it happens

Trigger: GetLatestSavePoint reads the savepoint document from CouchDB and the jsonValue bytes cannot be unmarshaled into couchSavepointData (corrupt bytes, wrong document type, truncated JSON).

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

Related errors


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/f6feb8336e1fe2ae. Report an issue: GitHub.