hyperledger/fabric · warning

maintenance mode

Error message

maintenance mode

What it means

ErrMaintenanceMode is returned when the orderer is in maintenance mode (channelconfig ConsensusType.State != NORMAL, typically during consensus-type migration) and a normal (non-config) transaction is submitted. It is returned wrapped via errors.WithMessage("normal transactions are rejected") in StandardChannel.ProcessNormalMsg and mapped by ClassifyError to SERVICE_UNAVAILABLE.

Source

Thrown at orderer/common/msgprocessor/msgprocessor.go:36

const (
	// These should eventually be derived from the channel support once enabled
	msgVersion = int32(0)
	epoch      = 0
)

var logger = flogging.MustGetLogger("orderer.common.msgprocessor")

// ErrChannelDoesNotExist is returned by the system channel for transactions which
// are not for the system channel ID and are not attempting to create a new channel
var ErrChannelDoesNotExist = errors.New("channel does not exist")

// ErrPermissionDenied is returned by errors which are caused by transactions
// which are not permitted due to an authorization failure.
var ErrPermissionDenied = errors.New("permission denied")

// ErrMaintenanceMode is returned when transactions are rejected because the orderer is in "maintenance mode",
// as defined by ConsensusType.State != NORMAL. This typically happens during consensus-type migration.
var ErrMaintenanceMode = errors.New("maintenance mode")

// Classification represents the possible message types for the system.
type Classification int

const (
	// NormalMsg is the class of standard (endorser or otherwise non-config) messages.
	// Messages of this type should be processed by ProcessNormalMsg.
	NormalMsg Classification = iota

	// ConfigUpdateMsg indicates messages of type CONFIG_UPDATE.
	// Messages of this type should be processed by ProcessConfigUpdateMsg.
	ConfigUpdateMsg

	// ConfigMsg indicates message of type CONFIG.
	// Messages of this type should be processed by ProcessConfigMsg
	ConfigMsg

	// UnsupportedMsg indicates a message of type ORDERER_TRANSACTION, which is no longer supported, since support for

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Pause client transaction submission until the consensus-type migration completes.
  2. Finish the migration steps and submit the config update setting ConsensusType State back to NORMAL.
  3. Verify the channel's consensus state (configtxlator or channel config) before resuming traffic.
  4. Retry the transaction with backoff once the orderer returns SERVICE_UNAVAILABLE→OK.

Example fix

// before
// client keeps sending txs during migration
// after
// wait for ConsensusType.State == NORMAL, then resume send(env)
Defensive patterns

Strategy: retry

Validate before calling

// check consensus state via fetched channel config
var ct orderer.ConsensusType
proto.Unmarshal(ordererGroup.Values["ConsensusType"].Value, &ct)
ready := ct.State == orderer.ConsensusType_STATE_NORMAL

Try / catch

err := broadcastClient.Send(env)
if errors.Is(err, msgprocessor.ErrMaintenanceMode) || statusOf(err) == cb.Status_SERVICE_UNAVAILABLE {
    // migrate paused traffic; retry with backoff until channel returns to NORMAL
}

Prevention

When it happens

Trigger: Broadcasting an ordinary endorsement transaction to a channel whose consensus type state is MAINTENANCE; only config updates related to the migration are accepted at that time.

Common situations: Applications continuing to send transactions while admins perform a Raft-to-BFT migration; forgetting to complete the migration and return the channel to NORMAL state.

Related errors


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