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

  1. If the node's ledger is actually current, this is expected — no action needed
  2. Verify the cluster is healthy and a quorum of up-to-date orderers is reachable
  3. If most peers are lagging, restart/repair them so targetHeight reflects the real cluster height
  4. 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

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


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