hyperledger/fabric · error
channel %s doesn't exist
Error message
channel %s doesn't exist
What it means
requestContext successfully extracted a channel name from the message, but c.Chan2Members contains no membership mapping for that channel. The orderer's cluster communication layer only knows about channels it is a member of; a message referencing an unknown channel cannot be attributed to a valid member set, so it is rejected.
Source
Thrown at orderer/common/cluster/comm.go:98
return err
}
return c.H.OnConsensus(reqCtx.channel, reqCtx.sender, request)
}
// requestContext identifies the sender and channel of the request and returns
// it wrapped in a requestContext
func (c *Comm) requestContext(ctx context.Context, msg proto.Message) (*requestContext, error) {
channel := c.ChanExt.TargetChannel(msg)
if channel == "" {
return nil, errors.Errorf("badly formatted message, cannot extract channel")
}
c.Lock.RLock()
mapping, exists := c.Chan2Members[channel]
c.Lock.RUnlock()
if !exists {
return nil, errors.Errorf("channel %s doesn't exist", channel)
}
cert := util.ExtractRawCertificateFromContext(ctx)
if len(cert) == 0 {
return nil, errors.Errorf("no TLS certificate sent")
}
stub := mapping.LookupByClientCert(cert)
if stub == nil {
return nil, errors.Errorf("certificate extracted from TLS connection isn't authorized")
}
return &requestContext{
channel: channel,
sender: stub.ID,
}, nil
}
// Remote obtains a RemoteContext linked to the destination node on the contextView on GitHub (pinned to 2736b63f8f)
Solutions
- Verify this orderer actually belongs to the named channel (check channel participation / genesis block configuration)
- Ensure the channel config update that added this orderer was committed and the orderer pulled it
- Check the sender's channel name spelling and that it targets the correct channel
- Restart the orderer after channel creation so Chan2Members is repopulated from the ledger
Example fix
// before (sender sends for a channel the orderer isn't part of)
clusterClient.Step(ctx, makeEnvelope("channel-unknown", msg))
// after — verify channel membership first
if ordererIsMemberOf("mychannel") {
clusterClient.Step(ctx, makeEnvelope("mychannel", msg))
} Defensive patterns
Strategy: validation
Validate before calling
// Sender-side: only dispatch to channels the target orderer belongs to
if _, err := os.Stat(channelLedgerPath(targetOrderer, channelName)); err != nil {
return fmt.Errorf("orderer %s is not a member of channel %s", targetOrderer, channelName)
}
// Receiver-side guard: check channel participation before Step
curl -s http://orderer:8443/participation/v1/channels | jq '.channels[].name' Prevention
- Verify channel participation (osnadmin / participation API) after every channel change
- Keep channel names consistent from config snapshot, never hardcode strings
- Commit config updates that add an orderer before routing traffic to it
- Monitor orderer logs for 'channel ... doesn't exist' as a config-drift signal
When it happens
Trigger: DispatchSubmit or DispatchConsensus delivers a message whose channel header names a channel this orderer has no mapping for — e.g. the channel was never joined, was removed, or the channel name is misspelled by the sender.
Common situations: An orderer added to a channel without the config update reaching it; sender and receiver disagree on channel name due to config drift; stale nodes sending traffic for deleted channels; genesis/system channel differences after upgrades.
Related errors
- node %d doesn't exist in channel %s's membership
- node identity is missing in channel
- specified --channelID %s does not match channel ID %s in con
- specified --channelID %s does not match channel ID %s in con
- OrdererOrg config does not allow sub-groups
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/2bc3be9e2dd9f58d.
Report an issue: GitHub.