hyperledger/fabric · info
already at target height of %d
Error message
already at target height of %d
What it means
Raised in BFTSynchronizer.synchronize when the local ledger height (startHeight) already meets or exceeds the target height computed from remote peers. As with the raft-style synchronizer, this indicates the node needs no replication; Sync then falls back to returning state from the local ledger. It is a normal, benign outcome rather than a fault.
Source
Thrown at orderer/consensus/smartbft/synchronizer_bft.go:97
// Buffer return the internal SyncBuffer for testability.
func (s *BFTSynchronizer) Buffer() *SyncBuffer {
s.mutex.Lock()
defer s.mutex.Unlock()
return s.syncBuff
}
func (s *BFTSynchronizer) synchronize() (*types.Decision, error) {
// === We probe all the endpoints and establish a target height, as well as detect the self endpoint.
targetHeight, myEndpoint, err := s.detectTargetHeight()
if err != nil {
return nil, errors.Wrapf(err, "cannot get detect target height")
}
startHeight := s.Support.Height()
if startHeight >= targetHeight {
return nil, errors.Errorf("already at target height of %d", targetHeight)
}
// === Create a buffer to accept the blocks delivered from the BFTDeliverer.
capacityBlocks := max(uint(s.LocalConfigCluster.ReplicationBufferSize)/uint(s.Support.SharedConfig().BatchSize().AbsoluteMaxBytes), 100)
s.mutex.Lock()
s.syncBuff = NewSyncBuffer(capacityBlocks)
s.mutex.Unlock()
// === Create the BFT block deliverer and start a go-routine that fetches block and inserts them into the syncBuffer.
bftDeliverer, err := s.createBFTDeliverer(startHeight, myEndpoint)
if err != nil {
return nil, errors.Wrapf(err, "cannot create BFT block deliverer")
}
go bftDeliverer.DeliverBlocks()
defer bftDeliverer.Stop()
// === Loop on sync-buffer and pull blocks, writing them to the ledger, returning the last block pulled.View on GitHub (pinned to 2736b63f8f)
Solutions
- If the node's ledger is actually current, this is expected — no action needed
- Verify the cluster is healthy and a quorum of up-to-date orderers is reachable
- If most peers are lagging, restart/repair them so targetHeight reflects the real cluster height
- If the node is wrongly ahead (restored snapshot), follow operator procedures to realign the ledger with the cluster
Defensive patterns
Strategy: type-guard
Validate before calling
// Skip sync when remote max height <= local height
if maxRemoteHeight(probe(consenters)) <= support.Height() {
// already caught up; no sync needed
} Try / catch
resp := bftSynchronizer.Sync()
// 'already at target height' is benign: verify final height matches expectation
if resp.Latest.Height >= support.Height()-1 {
// healthy; sync correctly reported nothing to do
} Prevention
- Treat as info-level, expected behavior on caught-up nodes
- Keep a quorum of current peers online so probes reflect real cluster height
- Verify backup-restore procedures never leave a node ahead of the live cluster
- Track local vs cluster heights in dashboards
When it happens
Trigger: synchronize() runs, detectTargetHeight succeeds (after deleting this node's own endpoint from the height map), and startHeight := s.Support.Height() >= targetHeight — i.e. remote reachable peers are not ahead of this node.
Common situations: Caught-up orderer during routine Sync calls; remaining reachable peers lag behind this node (most cluster offline); node previously restored from a newer backup; transiently probing only slow peers.
Related errors
- already at height of %d
- pending config does not match calculated expected config
- failed creating a new BFTChain
- cannot get HeightsByEndpoints
- no cluster members to synchronize with
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/d54701acb2ade613.
Report an issue: GitHub.