hyperledger/fabric · error

%d out of %d nodes are alive, configuration will result in q

Error message

%d out of %d nodes are alive, configuration will result in quorum loss

What it means

The etcdraft chain refuses config updates that would leave the raft cluster unable to maintain quorum (orderer/consensus/etcdraft/chain.go:1546). After computing membership changes, UnacceptableQuorumLoss compares currently alive nodes against the new effective quorum; if removing nodes would make the alive count insufficient, the update is rejected. This protects the cluster from permanent unavailability.

Source

Thrown at orderer/consensus/etcdraft/chain.go:1546

	c.raftMetadataLock.RUnlock()

	dummyOldConsentersMap := CreateConsentersMap(dummyOldBlockMetadata, oldMetadata)
	changes, err := ComputeMembershipChanges(dummyOldBlockMetadata, dummyOldConsentersMap, newMetadata.GetConsenters())
	if err != nil {
		return err
	}

	// new config metadata was verified above. Additionally need to check new consenters for certificates expiration
	for _, c := range changes.AddedNodes {
		if err := validateConsenterTLSCerts(c, verifyOpts, false); err != nil {
			return errors.Wrapf(err, "consenter %s:%d has invalid certificates", c.GetHost(), c.GetPort())
		}
	}

	active := c.ActiveNodes.Load().([]uint64)
	if changes.UnacceptableQuorumLoss(active) {
		c.logger.Debugf("%d out of %d nodes are alive - %+v", len(active), len(dummyOldConsentersMap), active)
		return errors.Errorf("%d out of %d nodes are alive, configuration will result in quorum loss", len(active), len(dummyOldConsentersMap))
	}

	return nil
}

// StatusReport returns the ConsensusRelation & Status
func (c *Chain) StatusReport() (types.ConsensusRelation, types.Status) {
	c.statusReportMutex.Lock()
	defer c.statusReportMutex.Unlock()

	return c.consensusRelation, c.status
}

func (c *Chain) suspectEviction() bool {
	if c.isRunning() != nil {
		return false
	}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Bring the remaining nodes back online so enough nodes are alive before submitting the update
  2. Shrink the cluster in stages (e.g. 5→4→3), re-verifying liveness at each step
  3. Remove the nodes from the config first while they are still running and reachable
  4. Check ActiveNodes liveness via the orderer metrics/logs before submitting

Example fix

// before: removing 3 of 5 consenters while 2 are down
ConfigUpdate: remove orderer3, orderer4, orderer5 (only 2 alive)
// after: staged removal
Step 1: restart orderer4/orderer5, then remove orderer5 only; Step 2: remove orderer4 once quorum-verified
Defensive patterns

Strategy: validation

Validate before calling

active := c.ActiveNodes.Load().([]uint64)
if changes.UnacceptableQuorumLoss(active) {
    return fmt.Errorf("abort update: only %d of %d nodes alive would break quorum", len(active), len(changes.TotalNodes))
}

Type guard

func safeToRemove(currentAlive, currentTotal, removing int) bool {
    remaining := currentTotal - removing
    quorum := remaining/2 + 1 // majority quorum
    return currentAlive-0 >= quorum
}

Try / catch

if err := submitConfigUpdate(cfg); err != nil {
    if strings.Contains(err.Error(), "quorum loss") {
        // restart dead nodes or remove fewer consenters per step
    }
}

Prevention

When it happens

Trigger: A config update removes consenters such that the number of currently responsive nodes would fall below the quorum required by the resulting cluster size (e.g. shrinking 5-node cluster to 3 while only 2 nodes are alive).

Common situations: Decommissioning several orderers at once during infrastructure cleanup; removing nodes whose containers are stopped; maintenance that temporarily shuts down nodes before submitting the shrink update.

Related errors


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