hyperledger/fabric · error

failed to marshal savepoint data

Error message

failed to marshal savepoint data

What it means

encodeSavepoint serializes couchSavepointData (block and tx number) to JSON for persisting the ledger's latest savepoint in CouchDB. Failures are wrapped with 'failed to marshal savepoint data'. With plain uint64 fields this essentially cannot fail unless the struct or JSON library behaves abnormally.

Source

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

	ChannelName string `json:"ChannelName"`
	// namespace to namespaceDBInfo mapping
	NamespaceDBsInfo map[string]*namespaceDBInfo `json:"NamespaceDBsInfo"`
}

type namespaceDBInfo struct {
	Namespace string `json:"Namespace"`
	DBName    string `json:"DBName"`
}

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)

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Read the wrapped cause from the Errorf log to identify the failing field in couchSavepointData
  2. Ensure couchSavepointData contains only JSON-serializable primitive fields
  3. Retry the commit; if persistent, rebuild the peer binary from a stock Fabric release

Example fix

// before
type couchSavepointData struct {
    BlockNum uint64
    Conn chan struct{} // not serializable
}
// after
type couchSavepointData struct {
    BlockNum uint64
    TxNum    uint64
}
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := json.Marshal(savepointDoc); err != nil {
    return fmt.Errorf("savepoint doc not serializable: %w", err)
}

Try / catch

doc, err := encodeSavepoint(height)
if err != nil {
    logger.Errorf("savepoint encode failed: %+v", err)
    return err
}

Prevention

When it happens

Trigger: recordSavepoint calls encodeSavepoint and json.Marshal returns an error while constructing the savepoint document for the given height.

Common situations: Custom builds where couchSavepointData gained non-JSON-serializable fields (channels, funcs, cycles), or corrupted dependencies/JSON library behavior.

Related errors


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